记忆装饰器保持存储的值
Posted
技术标签:
【中文标题】记忆装饰器保持存储的值【英文标题】:Memoizing decorator keeping stored values 【发布时间】:2017-11-18 13:09:31 【问题描述】:我有一个看起来像这样的记忆装饰器:
def memoize(obj):
from functools import wraps
cache =
@wraps(obj)
def memoizer(*args, **kwargs):
if args not in cache:
cache[args] = obj(*args, **kwargs)
return cache[args]
return memoizer
但是,我不确定此函数是否正常工作,因为在我看来,每次调用装饰函数时,它都会将 cache
重新创建为空字典。当我用一个简单的斐波那契函数对其进行测试时,它似乎可以正确记忆。那么cache
不是每次都重新创建吗?
包含此行的python wiki has a version:
cache = obj.cache =
所以我不确定这是做什么的。我猜python函数是对象,所以它正在创建一个与函数关联的新属性,并且每次调用函数时都是公开可用/可用的。
在任一版本中,如果我重复调用函数,如在递归定义中或仅通过重复调用,缓存将如何处理?它是否与函数相关联,它是成为“全局”变量还是其他什么?
【问题讨论】:
“每次” - 每个什么?每次调用memoize
时它都会创建一个新的 cache
,但并不是每次调用 memoized 函数时都会调用 memoize
。
【参考方案1】:
1) 每次调用memoize()
都会创建一个新的cache
。但是memoize()
只为每个修饰函数调用一次。因此每个装饰函数都有自己的cache
。
2) 关于cache = obj.cache =
:
函数也是 Python 中的对象。正如您已经假设的那样,obj.cache =
将在包装函数上创建一个新属性。本地缓存对象和包装函数上的缓存属性将指向同一个字典。
这并不意味着cache
是一个全局变量——它在 memoize 函数中是局部的,它是函数的一个属性。
如果你有一个修饰函数f
,你可以在全局范围内访问f.cache
,如果这就是你对全局变量的意思的话。
如果您重复调用修饰函数,它将始终访问相同的 cache
特定于修饰函数。
解决没有被“记忆”的内部功能。
def outer():
# do not add the decorator here!
def inner():
return 5
if(not hasattr(outer, "inner")):
# the "@" decorator is only syntactical sugar,
# we can simply call the decorator function to wrap another function
outer.inner = memoize( inner )
outer.inner()
return outer.inner()
outer()
outer()
【讨论】:
如果您提到 closures 并且memoizer
关闭 其环境(包括 cache
和 obj
),此答案可能会更好. (此外,“它自己的”中没有撇号。)
简要跟进:我测试了在另一个函数中定义的修饰函数,因此每次缓存都被重置。这是因为函数定义在超出范围后消失了吗?
@qwr 完全正确。如果您将print("memoize called", obj)
添加到 memoize 函数中,您将看到如果再次调用外部函数,内部函数会发生变化。但是如果你在外部函数中多次调用内部函数,它仍然在同一个范围内。
@qwr 我更新了答案,以展示如何通过使内部函数成为外部函数的属性来解决未记忆的内部函数。以上是关于记忆装饰器保持存储的值的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Python 的装饰器语法比普通的包装器语法提供更快的记忆代码?
具有装饰器的模拟功能。再次使用相同的装饰器来装饰 Mock 对象并使其保持为 Mock