如何在Python中使用记忆化技术来优化属性?

5
考虑以下简单示例:
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.a”和“self.b”不变,我们就不需要重复计算该属性。
仅当“self.a”或“self.b”发生更改时,才应重新计算该属性 - 有没有简单的方法可以做到这一点?
3个回答

14

Python3:

from functools import lru_cache as memoized

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

python 3.8

from functools import cached_property

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

2
还有 @cached_property(从3.8开始)。我不确定它与在@cache上叠加@property有何区别,以及哪种用例更好。文档(https://docs.python.org/3/library/functools.html#functools.cached_property)在一定程度上对其进行了解释。 - Tom
2
这种方法在这里不适用。因为ab不是函数的参数,所以它们不会被记忆化。即使ab改变,你仍然会得到相同的结果。 - kindall

1

对于 ab 也使用属性,并在设置器中清除缓存:

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

或者如果您想要更通用的东西,也可以查看这个: 在对象中存储计算值

编辑:最近有人建议在__init__中添加self._sum = None以“避免访问和错误”,但实际上这并不必要 - __init__调用a.setter,它调用_clearsum,该方法设置了_sum属性,因此保证了无论如何都会创建self._sum


这是实际执行问题要求的代码。 - kindall

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

这并不考虑ab的值,因为它们不是函数的参数。你只会得到相同的结果。事实上,你的示例表明了这一点。问题是希望在更新实例的属性时更新属性。 - kindall
@kindall,我认为我的回答没有解决问题,需要删除吗? - AthulMuralidhar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接