Очистка 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)
Другие вопросы по тегам