为什么在使用常规方法时functools.lru_cache不缓存__call__

时间:2018-11-22 02:38:15

标签: python caching magic-methods

我一直在尝试按照this answer所述使functools.lru_cache实例特定,但是在__call__方法上使用时,他们的解决方案失败了。

class test:
    def __init__(self):
        self.method = lru_cache()(self.method)
        self.__call__ = lru_cache()(self.__call__)

    def method(self, x):
        print('method', end=' ')
        return x

    def __call__(self, x):
        print('__call__', end=' ')
        return x

b = test()
# b.method is cached as expected
print(b.method(1)) # method 1
print(b.method(1)) # 1

# __call__ is executed every time
print(b(1)) # __call__ 1
print(b(1)) # __call__ 1

因此,使用此方法包装时,__call__的结果不会被缓存。 __call__上的缓存甚至不注册已调用的函数,并且不可散列的值也不会引发错误。

print(b.method.cache_info())
# CacheInfo(hits=1, misses=1, maxsize=128, currsize=1)
print(b.__call__.cache_info())
# CacheInfo(hits=0, misses=0, maxsize=128, currsize=0)

print(b.call({})) # __call__ {}
print(b.method({})) # ... TypeError: unhashable type: 'dict'

2 个答案:

答案 0 :(得分:0)

这是由于类属性和实例属性之间的差异。当访问属性(例如method)时,python首先检查实例属性。如果您尚未分配给self.method,它将找不到一个。然后检查类属性,该属性等效于self.__class__.method。分配给self.method不会更改此函数的值,只会更新实例属性。

但是,b(1)变成b.__class__.__call__(b, 1),它使用__call__的原始 class 定义,并且b.__call__(1)的缓存方式与{{ 1}},因为它使用了 instance 定义。

答案 1 :(得分:0)

原始答案真的很好。

我正在附上该问题的另一种解决方案。 methodtools.lru_cache将按预期工作。

from methodtools import lru_cache

class test:
    @lru_cache
    def __call__(self, x):
        print('__call__', end=' ')
        return x

它需要通过pip安装methodtools

pip安装方法工具