Почему functools.lru_cache не кэширует __call__ при работе с обычными методами - PullRequest
0 голосов
/ 22 ноября 2018

Я пытался сделать 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 голосов
/ 05 мая 2019

Оригинальный ответ действительно хорош.

Я прилагаю другое решение проблемы.methodtools.lru_cache будет работать, как вы ожидаете.

from methodtools import lru_cache

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

Требуется установить methodtools через pip:

pip install methodtools

0 голосов
/ 22 ноября 2018

Это связано с разницей между атрибутами класса и атрибутами экземпляра.При доступе к атрибуту (например, method) Python сначала проверяет атрибут экземпляра.Если вы не присвоили self.method, он не найдет его.Затем проверяются атрибуты класса, что эквивалентно self.__class__.method.Значение этой функции не изменяется путем присвоения self.method, который только обновляет атрибут экземпляра.

Однако b(1) становится b.__class__.__call__(b, 1), который использует исходное определение class __call__ и b.__call__(1) будут кэшироваться так же, как и method, поскольку он использует определение instance .

...