How to memoize a property in Python? - 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?

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

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.

there is a module that does this. Pypi link here: https://pypi.org/project/memoized-property/
For the above code I have this with using the module:
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

Related

Using getter and setter with three attributes

Please, explain what I do not understand about getters and setters, that this code catch the exception already when I'm trying to instance the Test class?
The same, as it seems to me, have worked here.
My goal is to update c depending on a and b, where all these properties should be accessible from outside of the class, i.e. public fields, as far as I understand.
class Test:
def __init__(self, p1=50, p2=20):
self.a = p1
self.b = p2
#property
def a(self):
return self._a
#a.setter
def a(self, val):
self._a = val
self._c = self.b - val // 5
#property
def b(self):
return self._b
#b.setter
def b(self, val):
self._b = val
#property
def c(self):
return self._c
>>> c = Test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "...\getter_and_setter.py", line 3, in __init__
self.a = p1
File "...\getter_and_setter.py", line 12, in a
self._c = self.b - val // 5
File "...\getter_and_setter.py", line 16, in b
return self._b
AttributeError: 'Test' object has no attribute '_b'
There is flaw in your implementation.
Setting a depends on b being already set. If you swap the 2 assignment statements in the __init__ it will solve your current problem. However note there is big flaw in your implementation. If you change b that change will not be reflected in c.
There is no need to use getters and setters for a and b.
class Test:
def __init__(self, p1=50, p2=20):
self.a = p1
self.b = p2
#property
def c(self):
return self.b - self.a // 5

Python: how to count the access of an instance variable

I have a python class as below.
class A(object):
def __init__(self, logger):
self.b = B()
self.logger = logger
def meth1(self):
self.b.mymethod1()
def meth2(self):
self.meth1()
self.b.mymethod2()
.........
class B(object):
---------
How can I count how many time I accessed self.b variable on the invocation of meth2() or any method of class A. Is there any way, I can log the usage of self.b variable?
make 'b' a property and and increase the counter corresponding to be in the setter.
#property
def b(self):
self.b_counter += 1
return self._b
and in your class replace b with _b
If you don't want to make a property, you can log the read/write access using __getattribute__ (not __getattr__ since b exists and would not be called) and __setattr__:
class A(object):
def __init__(self):
# initialize counters first !
self.b_read_counter = 0
self.b_write_counter = 0
# initialize b
self.b = 12
def __getattribute__(self,attrib):
# log read usage
if attrib=="b":
self.b_read_counter+=1
# now return b value
return object.__getattribute__(self, attrib)
def __setattr__(self,attrib,value):
if attrib=="b":
self.b_write_counter+=1
return object.__setattr__(self, attrib,value)
a = A()
a.b = 23 # second write access (first is in the init method)
if a.b == 34: # first read access
print("OK")
if a.b == 34:
print("OK")
if a.b == 34: # third read access
print("OK")
print(a.b_read_counter)
print(a.b_write_counter)
result:
3
2
You can use descriptors for this or just make a property which is basically is descriptor.
class A(object):
def __init__(self, logger):
self._b = B()
self._b_counter = 0
self.logger = logger
#property
def b(self):
self._b_counter += 1
return self._b
def meth1(self):
self.b.mymethod1()
def meth2(self):
self.meth1()
self.b.mymethod2()
You can use property, somtehing like:
class A(object):
def __init__(self, logger):
self._b = B()
self._count = 0
self.logger = logger
#property
def b(self):
self._count += 1
return self._b
...
...

Python Inheritance: what is the difference?

Given a parent class 'A'
Class A(object):
def __init__(self,a,b):
self.a = a
self.b = b
What is the difference between making a subclass 'B' among the below options
Option 1
Class B(A):
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
Option 2
Class B(A):
def __init__(self,a,b,c):
A.__init__(self, a, b)
self.c = c
In this case, none. But what if A.__init__ did loads of complex logic? You don't want to have to duplicate all that in B.
An enhancement on Option 2 is to use the super() function:
class B(A):
def __init_(self,a,b,c):
super(B, self).__init__(a, b)
self.c = c
Your first option initializes members of class A (a and b) as if it was in class B.
The second option uses constructor of A to initialize members of A before initializing members of B.
A better approach to design classes in Python would be
class A(object):
def __init__(self, a, b):
self._a = a
self._b = b
#property
def a(self):
return self._a
#property
def b(self):
return self._b
class B(A):
def __init__(self, a, b, c):
super(B, self).__init__(a, b)
self._c = c
#property
def c(self):
return self._c
The _ in member names mentions that the members should not be accessed directly. The decorator #property provides direct accessors for the members.
Note that members are read only. This class have no setters specified. For example, setter for c can be declared as follows
#c.setter
def c(self, c):
self._c = c

Using property decorator to set and get object values in Python [duplicate]

Could anyone find a problem with this #property decorator? I cannot seem to get it to assert correctly. I'm sure I'm doing some really simple thing wrong, but can anyone point my tired eyes in the right direction please?
class A:
def __init__(self):
self.a = 0
self._b = 0
#property
def b(self):
return self.b
#b.getter
def b(self):
if self._b is None:
return 0
return self._b
#b.setter
def b(self, val):
self._b = (val * 20)
def test_getter_setter():
obj = A()
obj.a = 1
#obj.b = 2
print obj.a, obj.b
obj.b = 2
print obj.a, obj.b
assert obj.b == 40
test_getter_setter()
The #property decorator only works on new style classes. Inherit from object:
class A(object):
With that change your test function passes.

How to implement this mechanism:

i want to implement a dynamic relation mechanism with python something like:
a:=10
b:=30
c:=a+b
print c
a+=20
print c
output:
40
60
c is always result of a+b.
so if a or b change, then c automatically updates value. i write a code in C# and do this by set and get mechanism. now want to translate it to python code for using in another program (FontLab Studio 5). I'm not so familiar with Python. does he have a get,set feature like C#? if not ho to implement one?
This is probably overkill, but it illustrates how you should create getters/setters in Python and achieve the functionality you want:
class Calc(object):
def __init__(self, a = 0, b = 0):
self._a = a
self._b = b
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
#property
def c(self):
return self._a + self._b
def __str__(self):
return str(self.c)
calc = Calc()
calc.a = 1
calc.b = 2
print calc.c
calc.a += 10
print calc.c
If you don't want to make a and b a property, the code can be simplified:
class Calc(object):
def __init__(self, a = 0, b = 0):
self.a = a
self.b = b
#property
def c(self):
return self.a + self.b
def __str__(self):
return str(self.c)
In your situation, c actually is a function which must be called.
You could use something like this:
a = 10
b = 30
c = lambda: a + b
print c()
a += 20
print c()
If you dislike that the method call is made explicit for c, you could use a general Calc object, which hides this implementation:
class Calc(object):
def __init__(self):
object.__setattr__(self, '_params', dict())
def __getattr__(self, name):
param = self._params[name]
if callable(param):
return param()
else:
return param
def __setattr__(self, name, value):
self._params[name] = value
def __delattr__(self, name):
del self._params[name]
And then you could do:
c = Calc()
c.a = 10
c.b = 30
c.c = lambda: c.a + c.b
print c.c
c.a += 20
print c.c
New-style Python classes support properties.
something like this:
class C:
def __init__(self):
self.x = 0
self.y = 0
def get(self):
return self.x + self.y
def __str__(self):
return self.__unicode__()
def __unicode__(self):
return str(self.get())
c = C()
c.x = 1
print c
c.y =2
print c
With new style classes and annotations you can probably make it better.

Categories

Resources