Очистка lru_cache определенных методов при обновлении атрибута класса?
У меня есть объект с методом / свойством multiplier
, Этот метод вызывается много раз в моей программе, поэтому я решил использовать lru_cache()
на нем, чтобы улучшить скорость выполнения. Как и ожидалось, это намного быстрее:
Следующий код показывает проблему:
from functools import lru_cache
class MyClass(object):
def __init__(self):
self.current_contract = 201706
self.futures = {201706: {'multiplier': 1000},
201712: {'multiplier': 25}}
@property
@lru_cache()
def multiplier(self):
return self.futures[self.current_contract]['multiplier']
CF = MyClass()
assert CF.multiplier == 1000
CF.current_contract = 201712
assert CF.multiplier == 25
2-й assert
терпит неудачу, потому что кэшированное значение равно 1000 как lru_cache()
не знает, что основной атрибут current_contract
был изменен.
Есть ли способ очистить кеш при обновлении self.current_contract?
Спасибо!
2 ответа
Да, довольно просто: сделать current_contract
свойство read/write и очистите кеш в установщике свойства:
from functools import lru_cache
class MyClass(object):
def __init__(self):
self.futures = {201706: {'multiplier': 1000},
201712: {'multiplier': 25}}
self.current_contract = 201706
@property
def current_contract(self):
return self._current_contract
@current_contract.setter
def current_contract(self, value):
self._current_contract = value
type(self).multiplier.fget.cache_clear()
@property
@lru_cache()
def multiplier(self):
return self.futures[self.current_contract]['multiplier']
NB: я предполагаю, что ваш реальный вариант использования включает в себя дорогостоящие вычисления, а не просто точный поиск - иначе lru_cache
может быть немного излишним;)
Короткий ответ
Не очищайте кеш, когда
self.current_contract
обновлено. Это работает против кеша и отбрасывает информацию.
Вместо этого просто добавьте методы для и. Это научит кеш (или любое другое отображение), какие атрибуты важны для влияния на результат.
Наработанный пример
Здесь мы добавляем
__eq__
а также
__hash__
к вашему коду. Это сообщает кешу (или любому другому отображению), что current_contract является соответствующей независимой переменной:
from functools import lru_cache
class MyClass(object):
def __init__(self):
self.current_contract = 201706
self.futures = {201706: {'multiplier': 1000},
201712: {'multiplier': 25}}
def __hash__(self):
return hash(self.current_contract)
def __eq__(self, other):
return self.current_contract == other.current_contract
@property
@lru_cache()
def multiplier(self):
return self.futures[self.current_contract]['multiplier']
Непосредственным преимуществом является то, что при переключении между номерами контрактов предыдущие результаты сохраняются в кеше. Попробуйте переключиться между 201706 и 201712 сто раз, и вы получите 98 попаданий в кеш и 2 промаха кеша:
cf = MyClass()
for i in range(50):
cf.current_contract = 201712
assert cf.multiplier == 25
cf.current_contract = 201706
assert cf.multiplier == 1000
print(vars(MyClass)['multiplier'].fget.cache_info())
Это печатает:
CacheInfo(hits=98, misses=2, maxsize=128, currsize=2)