我最近在 Python 装饰器库的装饰器中遇到了一种技术,memoized
它允许它支持实例方法:
import collections
import functools
class memoized(object):
'''Decorator. Caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned
(not reevaluated).
'''
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if not isinstance(args, collections.Hashable):
# uncacheable. a list, for instance.
# better to not cache than blow up.
return self.func(*args)
if args in self.cache:
return self.cache[args]
else:
value = self.func(*args)
self.cache[args] = value
return value
def __repr__(self):
'''Return the function's docstring.'''
return self.func.__doc__
def __get__(self, obj, objtype):
'''Support instance methods.'''
return functools.partial(self.__call__, obj)
正如文档字符串中所解释的那样,该__get__
方法是使装饰器支持实例方法的“魔术发生”的地方。以下是一些表明它有效的测试:
import pytest
def test_memoized_function():
@memoized
def fibonacci(n):
"Return the nth fibonacci number."
if n in (0, 1):
return n
return fibonacci(n-1) + fibonacci(n-2)
assert fibonacci(12) == 144
def test_memoized_instance_method():
class Dummy(object):
@memoized
def fibonacci(self, n):
"Return the nth fibonacci number."
if n in (0, 1):
return n
return self.fibonacci(n-1) + self.fibonacci(n-2)
assert Dummy().fibonacci(12) == 144
if __name__ == "__main__":
pytest.main([__file__])
我想了解的是:这种技术究竟是如何工作的?它似乎非常普遍地适用于基于类的装饰器,我在我的回答中应用了它是否可以 numpy.vectorize 实例方法?.
到目前为止,我已经通过注释掉该方法并在子句__get__
之后放入调试器来对此进行调查。else
似乎每当您尝试使用数字作为输入调用它时self.func
,它都会引发 a :TypeError
> /Users/kurtpeek/Documents/Scratch/memoize_fibonacci.py(24)__call__()
23 import ipdb; ipdb.set_trace()
---> 24 value = self.func(*args)
25 self.cache[args] = value
ipdb> self.func
<function Dummy.fibonacci at 0x10426f7b8>
ipdb> self.func(0)
*** TypeError: fibonacci() missing 1 required positional argument: 'n'
据我了解,https://docs.python.org/3/reference/datamodel.html#object。get,定义你自己的__get__
方法以某种方式覆盖了你(在这种情况下)调用时发生的事情self.func
,但我正在努力将抽象文档与这个例子联系起来。谁能一步一步解释这个?