在更新类的属性时清除某些方法的lru_cache?

时间:2017-07-24 14:01:26

标签: python caching functools

我有一个带有方法/属性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时是否有办法清除缓存?

谢谢!

2 个答案:

答案 0 :(得分:2)

很简单:让current_contract成为读/写属性并清除属性的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可能有点矫枉过正;)

答案 1 :(得分:1)

简答

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)