使用__slots__在对象中使用Python缓存属性

时间:2017-02-07 12:23:57

标签: python caching

我正在尝试在使用__slots__属性定义的类中缓存计算量很大的属性。

任何想法,如何存储缓存供以后使用?当然,如果没有定义instance._cache,在__dict__中存储字典的常用方法将无效。由于多种原因,我不想将'_cache'字符串添加到__slots__

我在想这是否是全球罕见的用例之一。关于此事的任何想法或例子?

3 个答案:

答案 0 :(得分:2)

你不需要全球化;您可以将缓存存储为类属性,并将昂贵的属性定义为property

class Foo(object):
    __slots__ = ('a', 'b', 'c')
    expensive_cache = {}

    @property
    def expensive(self):
        if self not in self.expensive_cache:
            self.expensive_cache[self] = self._compute_expensive()
        return self.expensive_cache[self]

    def _compute_expensive(self):
        print("Computing expensive property for {}".format(self))
        return 3

f = Foo()
g = Foo()
print(f.expensive)
print("===")
print(f.expensive)
print("===")
print(g.expensive)

如果您运行此代码,则可以看到_compute_expensive仅在您第一次访问每个不同对象的expensive时运行一次。

$ python3 tmp.py
Computing expensive property for <__main__.Foo object at 0x102861188>
3
===
3
===
Computing expensive property for <__main__.Foo object at 0x1028611c8>
3

答案 1 :(得分:2)

那里没有魔法 - 你想存储一个值,所以你需要一个存储价值的地方。 您不能只决定“我的__slots__上没有额外的条目,因为它不优雅” - 您无需将其称为_cached: 给它你想要的任何名称,但这些缓存的值是你想要存在于每个对象的实例中的东西,因此你需要一个属性。

您可以在全局(模块级别)字典中缓存,其中密钥为id(self) - 但在删除实例时保持同步是一个非常令人头疼的问题。 (对于类级别的字典也是如此,它的进一步缺点仍然在实例上可见。)

TL; DR :“一种显而易见的方法”是拥有一个shadow属性,以“_”开头以保留您想要缓存的值,并在{{ 1}}。 (如果每个实例使用一个__slots__字典,则会失去_cached的主要优势,即每个实例不需要一个字典。)

答案 2 :(得分:-2)

像Borg模式这样的东西可以提供帮助。 您可以使用__init____new__方法更改实例的状态。