缓存装饰器Python

时间:2015-05-18 00:17:53

标签: python caching decorator

我正在观看Raymond Hettinger的精彩视频之一,我对装饰者的例子感到有点困惑:

def cache(func):
    saved={}
    @wraps(func)
    def newfunc(*args):
        if args in saved:
            return newfunc(*args) # should be return saved[args]?
        result = func(*args)
        saved[args]=result
        return result
    return newfunc

我不是特别关于装饰器的专家,但是在找到该项被缓存后没有返回对newfunc(* args)的调用会导致一个永远不会完成的递归循环吗?我认为这是假设返回已保存的[args](函数最终返回的结果,这是同样的事情,但我认为如果在缓存中找到一个项目它就不会到达那里。)

2 个答案:

答案 0 :(得分:2)

是的,这是一个错误。

如果您不确定,让我们测试一下:

def fib(n):
    if n < 2:
        return 1
    return fib(n-2) + fib(n-1)

print(fib(10))

@cache
def cfib(n):
    if n < 2:
        return 1
    return cfib(n-2) + cfib(n-1)

print(cfib(10))

第一个打印出89,第二个打印出来:

  File "rhcache.py", line 8, in newfunc
    return newfunc(*args) # should be return saved[args]?
  File "rhcache.py", line 8, in newfunc
    return newfunc(*args) # should be return saved[args]?
  # ... 997 more copies
RuntimeError: maximum recursion depth exceeded

但如果我们按照您的建议更改它,它会再次打印89。 (并且,如果你计时,它运行速度比非缓存版本快;如果你对它进行分析,它只会对真实函数进行10次调用;等等。)

完全如您所愿。

那么,我们学到了什么?甚至Raymond Hettinger也不会在未经测试的代码中偶尔出现错误,但他的代码足够干净,即使不运行它也很容易找到并解决问题。 :)

您可以向他发送电子邮件,在YouTube页面上添加评论,或在PyVideo页面上报告问题。

答案 1 :(得分:2)

你也可以使用@functools.lru_cache(maxsize=128, typed=False)¶ 这看起来像是:

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)