【发布时间】:2016-08-09 14:53:55
【问题描述】:
我的问题,为什么
我正在尝试为类方法 @cachedproperty 编写装饰器。我希望它表现得如此,当第一次调用该方法时,该方法被它的返回值替换。我还希望它表现得像 @property 这样就不需要显式调用它。基本上,它应该与@property 没有区别,只是 它更快,因为它只计算一次值然后存储它。我的想法是,这不会像在 __init__ 中定义它那样减慢实例化速度。这就是我想要这样做的原因。
我尝试了什么
首先,我尝试覆盖property 的fget 方法,但它是只读的。
接下来,我想我会尝试实现一个确实需要第一次调用但随后缓存值的装饰器。这不是我永远不需要调用的属性类型装饰器的最终目标,但我认为这将是一个更简单的问题,首先要解决。换句话说,对于一个稍微简单的问题,这是一个行不通的解决方案。
我试过了:
def cachedproperty(func):
""" Used on methods to convert them to methods that replace themselves
with their return value once they are called. """
def cache(*args):
self = args[0] # Reference to the class who owns the method
funcname = inspect.stack()[0][3] # Name of the function, so that it can be overridden.
setattr(self, funcname, func()) # Replace the function with its value
return func() # Return the result of the function
return cache
但是,这似乎不起作用。我对此进行了测试:
>>> class Test:
... @cachedproperty
... def test(self):
... print "Execute"
... return "Return"
...
>>> Test.test
<unbound method Test.cache>
>>> Test.test()
但是我收到一个关于类如何没有将自身传递给方法的错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method cache() must be called with Test instance as first argument (got nothing instead)
在这一点上,我和我对深度 Python 方法的有限知识非常困惑,我不知道我的代码哪里出错了,也不知道如何修复它。 (我之前从未尝试过写装饰器)
问题
如何编写一个装饰器,在第一次访问时返回调用类方法的结果(如 @property 所做的那样),并为所有后续查询替换为缓存值?
我希望这个问题不会太令人困惑,我试图尽可能地解释它。
【问题讨论】:
-
为什么不创建一个自定义描述符来返回缓存对象...?装饰器可以工作,但对于您正在尝试做的事情来说,它似乎是错误的工具。
-
您希望这是一个 instance 属性还是 class 属性?
Test.test将是类属性的语法,但听起来您希望这是一个实例。 -
请注意,Django 附带了这样一个装饰器,它的来源在这里:docs.djangoproject.com/en/1.9/_modules/django/utils/functional/…。它会在第一次调用 get() 时覆盖 instance.__dict__,以便值可以覆盖属性。
-
FTR:这种缓存方法第一次调用后返回值的原理叫做memoization。
-
@oliver:但这比单纯的记忆更进一步,因为该方法实际上被值替换作为优化。这也意味着该方法不能接受任何参数,否则它将不起作用。
标签: python caching decorator python-decorators