Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to memoize a property in Python?

Consider the following minimal example:

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

I would like to memoize sum such that if self.a and self.b doesn't change, then we don't need to keep recalculating the property.

The property should only be recalculated when either self.a or self.b has changed -- is there an simple way to do this?

like image 628
mchen Avatar asked Aug 28 '26 20:08

mchen


2 Answers

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
like image 74
Jay Avatar answered Aug 30 '26 10:08

Jay


Use properties for a and b too and clear up your cache in the setters:

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

Or if you want something a bit more generic, you can check this too: Storing calculated values in an object

Edit : someone recently suggested adding self._sum = None in __init__ to "avoid an error when accessing sum", but that's actually not necessary - __init__ invokes a.setter, which invokes _clearsum, which sets the _sum attribute, so it's garanteed self._sum will be created whatever.

like image 45
bruno desthuilliers Avatar answered Aug 30 '26 10:08

bruno desthuilliers



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!