更新 class 的属性时清除某些方法的 lru_cache?
Clearing lru_cache of certain methods when an attribute of the class is updated?
我有一个 method/property 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
第二个 assert
失败,因为缓存值为 1000,因为 lru_cache()
不知道基础属性 current_contract
已更改。
有没有办法在self.current_contract更新时清除缓存?
谢谢!
是的,非常简单:将 current_contract
设为 read/write 属性 并清除 属性 的 setter:
中的缓存
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']
注意:我假设您的实际用例涉及昂贵的计算,而不仅仅是字典查找 - 否则 lru_cache
可能有点矫枉过正 ;)
简答
更新self.current_contract
时不清除缓存。那是对缓存起作用并丢弃信息。
相反,只需为 __eq__
和 __hash__
添加方法。这将告诉缓存(或任何其他映射)哪些属性对影响结果很重要。
计算出的例子
这里我们将 __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)
我有一个 method/property 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
第二个 assert
失败,因为缓存值为 1000,因为 lru_cache()
不知道基础属性 current_contract
已更改。
有没有办法在self.current_contract更新时清除缓存?
谢谢!
是的,非常简单:将 current_contract
设为 read/write 属性 并清除 属性 的 setter:
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']
注意:我假设您的实际用例涉及昂贵的计算,而不仅仅是字典查找 - 否则 lru_cache
可能有点矫枉过正 ;)
简答
更新self.current_contract
时不清除缓存。那是对缓存起作用并丢弃信息。
相反,只需为 __eq__
和 __hash__
添加方法。这将告诉缓存(或任何其他映射)哪些属性对影响结果很重要。
计算出的例子
这里我们将 __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)