Почему functools.lru_cache не кэширует __call__ при работе с обычными методами
Я пытался сделать 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'
1 ответ
Это связано с разницей между атрибутами класса и атрибутами экземпляра. При доступе к атрибуту (например, method
) Python сначала проверяет наличие атрибута экземпляра. Если вы не назначены self.method
он не найдет. Затем проверяются атрибуты класса, что эквивалентно self.__class__.method
, Значение этой функции не изменяется путем присвоения self.method
, который только обновляет атрибут экземпляра.
Тем не мение, b(1)
становится b.__class__.__call__(b, 1)
который использует оригинальное определение класса __call__
а также b.__call__(1)
будет кэшироваться так же, как method
поскольку он использует определение экземпляра.
Оригинальный ответ действительно хорош.
Я прилагаю другое решение проблемы.methodtools.lru_cache
будет работать так, как вы ожидаете.
from methodtools import lru_cache
class test:
@lru_cache
def __call__(self, x):
print('__call__', end=' ')
return x
Требуется установить methodtools
через пункт:
pip install methodtools