Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么一种记忆策略比另一种慢?_Python_Fibonacci_Memoization_Python Decorators - Fatal编程技术网

Python 为什么一种记忆策略比另一种慢?

Python 为什么一种记忆策略比另一种慢?,python,fibonacci,memoization,python-decorators,Python,Fibonacci,Memoization,Python Decorators,所以这本关于回忆录的书让我很好奇。我运行自己的基准测试 1) 可变默认字典: 输出: 2) 同样的想法,但遵循“请求原谅比允许更容易”的原则: 输出: 我的问题 为什么2)比1)慢 编辑 正如@kevin在评论中所暗示的,我把装饰师搞错了,所以我把它拆了。剩余部分仍然有效!(我希望)捕获异常意味着堆栈跟踪可能非常昂贵: 例外情况在两种情况下非常有效: 试试看。。。最后 试试看。。。除非,前提是不引发异常 但是,当发生异常并捕获所需的堆栈跟踪时 增加了巨大的开销 方法一总共进行三次查找(n不

所以这本关于回忆录的书让我很好奇。我运行自己的基准测试

1) 可变默认字典: 输出:

2) 同样的想法,但遵循“请求原谅比允许更容易”的原则: 输出:

我的问题
  • 为什么2)比1)慢
编辑 正如@kevin在评论中所暗示的,我把装饰师搞错了,所以我把它拆了。剩余部分仍然有效!(我希望)

捕获异常意味着堆栈跟踪可能非常昂贵:

例外情况在两种情况下非常有效:

  • 试试看。。。最后
  • 试试看。。。除非
    ,前提是不引发异常
  • 但是,当发生异常并捕获所需的堆栈跟踪时
    增加了巨大的开销

    方法一总共进行三次查找(
    n不在dic中:
    ,插入
    dic[n]=
    并返回
    dic[n]
    )。第二种方法在最坏情况下也进行三次查找(检索尝试
    dic[n]=
    、插入
    dic[n]=
    和返回
    dic[n]
    ),此外还涉及异常处理

    如果一种方法与另一种方法做相同的工作,并且添加了一些东西,那么它显然不会更快,而且很可能会更慢

    当记忆有更多有用的机会时,考虑比较场景中的效率——即多次运行函数,以比较分摊的复杂性。这样,第二种方法的最坏情况发生的频率就会降低,并且您可以从一次查找中获得更少的收益

    版本1:

    def fibo(n, dic={}) :
        if n not in dic :
            if n in (0,1) :
                dic[n] = 1
            else :
                dic[n] = fibo(n-1)+fibo(n-2)
        return dic[ n ]
    
    for i in range(10000):
        fibo(i)
    
    版本2:

    def fibo(n, dic={}) :
        try :
            return dic[n]
        except :
            if n in (0,1) :
                dic[n] = 1
            else :
                dic[n] = fibo(n-1)+fibo(n-2)
            return dic[ n ]
    
    for i in range(10000):
        fibo(i)
    
    以及测试:

    C:\Users\Bartek\Documents\Python>Python-m timeit--“导入版本1”
    1000000个循环,最好3个:每个循环1.64 usec

    C:\Users\Bartek\Documents\Python>Python-m timeit--“导入版本2”
    1000000个循环,最好3个:每个循环1.6 usec


    当函数被更频繁地使用时,缓存会被更多的值填充,从而降低异常的可能性。

    这是一个非常不寻常的装饰器。通常,当您装饰一个函数时,在装饰器中的某个地方,您会在完成一些工作后调用该函数。但是你的装饰者从不调用
    f
    。当你修饰
    fibo
    时,你实际上在做“扔掉这个函数的定义,用示例1中的函数替换它”捕获异常(这意味着堆栈跟踪)可能非常昂贵:@DmitryBychenko我想你应该把它作为一个答案发布。原则“请求原谅比允许更容易”在哪里来自哪里0在编程上下文中听起来很不合理。@BartoszKP这是真的。大多数时候,用户可能会给你正确的东西。这就是为什么最好假设他们给了你正确的东西,如果异常是错误的,就处理它。这样就避免了
    hasattr
    如果输入d:
    的开销。这个原则经常使用的另一个领域是在尝试使用键/属性之前检查它们是否存在,这(有时)更可能被原谅。
    In [21]:
    
    %%timeit
    def fibo(n, dic={}) :
        try :
            return dic[n]
        except :
            if n in (0,1) :
                dic[n] = 1
            else :
                dic[n] = fibo(n-1)+fibo(n-2)
            return dic[ n ]
    fibo(30)
    
    10000 loops, best of 3: 46.8 µs per loop
    
    def fibo(n, dic={}) :
        if n not in dic :
            if n in (0,1) :
                dic[n] = 1
            else :
                dic[n] = fibo(n-1)+fibo(n-2)
        return dic[ n ]
    
    for i in range(10000):
        fibo(i)
    
    def fibo(n, dic={}) :
        try :
            return dic[n]
        except :
            if n in (0,1) :
                dic[n] = 1
            else :
                dic[n] = fibo(n-1)+fibo(n-2)
            return dic[ n ]
    
    for i in range(10000):
        fibo(i)