How can data remain persistent across multiple calls of decorated function?
以下函数将用作存储已计算值结果的修饰器。如果之前已经计算过参数,函数将返回存储在
1 2 3 4 5 6 7 8 9 | def cached(f): f.cache = {} def _cachedf(*args): if args not in f.cache: f.cache[args] = f(*args) return f.cache[args] return _cachedf |
我认识到(错误地)
1 2 3 4 5 6 7 8 | def cached(f): cache = {} # <---- not an attribute this time! def _cachedf(*args): if args not in cache: cache[args] = f(*args) return cache[args] return _cachedf |
我很难理解
有人能帮我理解,即使在返回
您正在这里创建一个闭包:函数
编辑:也许我应该添加一些关于这在Python中如何工作以及CPython如何实现这一点的详细信息。
让我们来看一个简单的例子:
1 2 3 4 5 6 | def f(): a = [] def g(): a.append(1) return len(a) return g |
交互式解释器中的示例用法
1 2 3 4 5 6 7 | >>> h = f() >>> h() 1 >>> h() 2 >>> h() 3 |
在编译包含函数
那么当调用函数
由于
Can anyone please help me understand how the cache variable still exists even after the _cachedf function is returned?
它与Python的引用计数垃圾收集器有关。
在销毁对缓存的所有引用之前,不会丢失缓存。您可以使用
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | >>> import time >>> def cached(f): ... cache = {} # <---- not an attribute this time! ... def _cachedf(*args): ... if args not in cache: ... cache[args] = f(*args) ... return cache[args] ... return _cachedf ... ... >>> def foo(duration): ... time.sleep(duration) ... return True ... ... >>> bob = cached(foo) >>> bob(2) # Takes two seconds True >>> bob(2) # returns instantly True >>> del bob # Deletes reference to bob (aka _cachedf) which holds ref to cache >>> bob = cached(foo) >>> bob(2) # takes two seconds True >>> |
对于记录来说,您试图实现的是一种叫memoization的方法,在decorator模式页面上有一个更完整的memoizing decorator,它执行相同的操作,但是使用decorator类。您的代码和基于类的decorator本质上是相同的,在存储之前,基于类的decorator检查哈希能力。
编辑(2017-02-02):@simingie评论:
这是因为
因为缓存是空的,找不到值,所以它重新运行底层函数。相反,做
1 2 3 4 5 6 | @cached def my_long_function(arg1, arg2): return long_operation(arg1,arg2) my_long_function(1,2) # incurs delay my_long_function(1,2) # doesn't |
如果您不熟悉装饰师,请看一下这个答案,了解上面的代码的含义。