У меня есть функция, которую мне нужно кэшировать. Аргументы функции — это объект obj
среди прочих. Однако я хотел бы периодически менять свойство объекта obj
.
Я пробовал использовать functools lru_cache
.
class TestObj:
def __init__(self, number):
self.number = number
@lru_cache(1024)
def print_number(obj):
return obj.number
obj = TestObj(1)
assert print_number(obj) == 1
obj.number = 2
assert print_number(obj) == 2
Я бы хотел, чтобы вторым утверждением утверждения было true
, так как я изменил базовое свойство объекта obj
Ваш пример в значительной степени противоречит цели lru_cache
.. Во всяком случае, единственный способ, который я могу придумать, это добавить print_number.cache_clear()
перед вашим вторым утверждением:
assert print_number(obj) == 1
obj.number = 2
print_number.cache_clear()
assert print_number(obj) == 2
Спасибо и извините за упрощенный пример.
Это общая проблема с изменяемыми объектами. Какую бы систему кэширования вы ни использовали, цель кэша — вернуть предыдущее значение (без его вычисления) при задании тех же параметров.
Здесь вы фактически передаете тот же самый объект (настоящая идентичность, заданная is
в Python), поэтому ни одна система кэширования не может догадаться, что ее кеш грязный. Вот распространенные обходные пути:
number
должно быть свойством и что TestObj
должно знать о кеше. тут не все так просто ИМХОprint_number
, а сделайте его оболочкой вокруг другой функции, вызывающей соответствующие неизменяемые члены TestObj. Тривиально здесьВам придется
def print_number(obj):
return do_print_number(obj.number)
@lru_cache(1024)
def do_print_number(number):
return number
Это будет работать при условии, что obj.number
является неизменяемым объектом, например, числом, строкой или кортежем, а не списком, набором или картой.
Спасибо за ваш подробный ответ.
Это не имеет никакого смысла. Вы можете просто написать
obj.number
вместоprint_number(obj)
. Можете ли вы придумать лучший пример?