如何在Python中记忆属性?

时间:2017-02-11 14:06:58

标签: python properties memoization

考虑以下最小例子:

class Foo(object):

    def __init__(self):
        self.b = self.a = 1

    @property
    def sum(self):
        print 'Recalculating sum'
        return self.a + self.b

foo = Foo()
print foo.sum
print foo.sum   # Prints 'Recalculating sum' even though neither a or b has changed since previous call
foo.a = 2
print foo.sum   # a has been changed to 2 so recalculation is necessary

我想记住sum,如果self.aself.b没有变化,那么我们就不需要继续重新计算财产。

只有在self.aself.b发生变化时才重新计算该属性 - 是否有一种简单的方法可以执行此操作?

3 个答案:

答案 0 :(得分:1)

python3:

from functools import lru_cache as memoized

@property
@memoized(maxsize=1)
def sum(self):
    return self.a + self.b

python 3.9

from functools import cache

@property
@cache
def sum(self):
    return self.a + self.b

答案 1 :(得分:0)

也使用ab的属性,并在setter中清理缓存:

class Foo(object):

    def __init__(self):
        self.a = 1
        self.b = 1

    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value
        self._clearsum()

     @property
    def b(self):
        return self._b

    @b.setter
    def b(self, value):
        self._b = value
        self._clearsum()

    def _clearsum(self):
        self._sum = None

    @property
    def sum(self):
        if self._sum is None:
            self._sum = self.a + self.b
        return self._sum

或者,如果您想要更通用的东西,您也可以检查: Storing calculated values in an object

修改:有人最近建议在self._sum = None中添加__init__以“在访问总和时避免错误”,但实际上没有必要 - __init__调用a.setter,这会调用_clearsum,设置_sum属性,因此保证self._sum将被创建。

答案 2 :(得分:0)

有一个模块可以做到这一点。 Pypi链接在这里:https://pypi.org/project/memoized-property/ 对于上面的代码,我可以使用模块来实现:

In [2]: from memoized_property import memoized_property                                                                                                       

In [3]: class test():  
   ...:         def __init__(self):  
   ...:             self.a = 0  
   ...:             self.b = 0  
   ...:        @memoized_property  
   ...:        def sum(self):  
   ...:           print('calculating...')  
   ...:           return self.a + self.b  

In [4]: t=test()                                                                                                                                              

calculating...
In [5]: t.sum                                                                                                                                                 
Out[5]: 0

In [7]: t.a=5                                                                                                                                                 

In [8]: t.sum                                                                                                                                                 
Out[8]: 0