pythonic class instance attribute calculated from other attributes - python

I have a class instance with attributes that are calculated from other attributes. The attributes will change throughout the life of the instance. All attributes are not necessarily defined when the object is initialized.
what is the pythonic way to calculate attributes from other attributes?
This is a simple example, the calculations have numerous input variables ("a" below) and calculations ("b" & "c").
a = something
b = function of a (a+5)
c = function of a and b (a*b)
I've tried numerous implementations. Here is a decent one to communicate my intention.
class CalcAttr(object):
def __init__(self):
self._a = None
self._b = None
self._c = None
#property
def a(self):
return self._a
#a.setter
def a(self,value):
self._a = value
#property
def b(self):
self.calc_b()
return self._b
#property
def c(self):
self.calc_c()
return self._c
def calc_b(self):
self._b = self._a + 5
def calc_c(self):
self._c = self._a * self._b
def test():
abc = CalcAttr()
a = 5
return abc.c
Note: t.c works if I first call t.b first.
> >>> t=abc.test()
> >>> t.c Traceback (most recent call last): File "<stdin>", line 1, in <module> File "abc.py", line 22, in c
> self.calc_c() File "abc.py", line 29, in calc_c
> self._c = int(self._a) * int(self._b) TypeError: int() argument must be a string or a number, not 'NoneType'
> >>> t.b 10
> >>> t.c 50
> >>>
Keep in mind most of the real calculations are dependent on multiple attribures (5-10 input variables & as many calculated ones).
My next iteration will include a "calculate_model" function that will populate all calculated attributes after checking that all inputs are defined. Maybe that will be the pyhonic answer?
Thanks!
Update - working solution
I created a method that calculates each attribute in order:
def calc_model(self):
self.calc_b()
self.calc_c()
Each calculated attribute calls that method
#property
def c(self):
self.calc_model()
return self._c
I'm not sure if this is proper, but it works as desired...

If I understand your question correctly, you should compute b and c in their getters. You should also probably require that the user passes a value for a in the initializer, since b and c can't be computed without a. Also, it doesn't seem like there is much of a reason to keep _a, _b, and _c around -- unless b and c are expensive to compute and you'd like to cache them.
For example:
class CalcAttr(object):
def __init__(self, a):
self.a = a
#property
def b(self):
return self.a + 5
#property
def c(self):
return self.a * self.b
Such that
>>> x = CalcAttr(42)
>>> x.c
1974

I understand what #jme suggested in the accepted answer is more elegant, but I still try to fix the original example and get it to work. Here is the code.
class CalcAttr(object):
def __init__(self):
self._a = None
self._b = None
self._c = None
#property
def a(self):
return self._a
#a.setter
def a(self,value):
self._a = value
#property
def b(self):
self.calc_b()
return self._b
#property
def c(self):
self.calc_c()
return self._c
def calc_b(self):
self._b = self._a + 5
def calc_c(self):
self._c = self.a * self.b
def test():
abc = CalcAttr()
abc.a = 5
return abc.c
test()
The code will work and 50 is the resulted value.

Related

Calling a method in setter in python

I want to implement a class property that is computed from other properties.
class Sum(object):
#property
def a(self):
return self._a
#a.setter
def a(self, val):
self._a = a
self._constructSeries()
#property
def b(self):
return self._b
#b.setter
def b(self, val):
self._b = b
self._constructSeries()
def _constructSeries(self):
# Some calculations involving a and b
self._series = function(a, b)
def __init__(self, a, b):
self.a = a
self.b = b
One way I know of is to define series as a property
#property
def series(self):
return fun(a,b)
But I want to avoid calling fun each and every time as it takes a lot of computations. What is the standard way to handle such a case?
If I got it right you want to be able to change a and b without computing the fun everytime but when you request the result of the fun is the most updated one, is it correct?
In such a case you can use property
def get_series(self):
if self._series is None or self.updated is False:
self._series = fun(self.a,self.b)
self.updated = True
return self._series
series = property(get_series)
And when you set a and b you update the flag
#property
def a(self):
self.updated = False
return self._a
#property
def b(self):
self.updated = False
return self._b
Then self.series returns the updated values but it runs fun just if the input changes from the last time it has been computed.

Conditions on class parameters

I am creating a Class qubit which has 2 parameters: a and b. I want to make sure everytime user changes parameters, the condition a^2 +b^2=1 must hold . If the condition does not hold then it is an invalid input.
A related Problem: Is there a way to run some function update() everytime the user changes parameters?
class Qubit(object):
def __init__(self):
self.a = 1
self.b = 0
Sure thing. Use propertys so you get getter/setter behavior:
class Qubit(object):
def __init__(self, a=1, b=0):
self._validate(a, b)
self._a = a
self._b = b
#staticmethod
def _validate(a, b):
if a ** 2 + b ** 2 != 1:
raise ValueError('{}^2 + {}^2 != 1'.format(a, b))
# Getter for `a`
#property
def a(self):
return self._a
# Setter for `a`
#a.setter
def a(self, value):
self._validate(a=value, b=self._b)
self._a = value
# (Repeat for `b`.)

Class with read-only attributes

I want to create a class with attributes that can be __setattr__-ed by its methods internally, so an attempt like self.attr = value would raise an AttributeError. This is what I have so far:
class MyClass():
def __init__(self, a, b, c):
self.a, self.b, self.c = a, b, c
def __repr__(self):
return '%r class with a=%s, b=%s, c=%s' % (self, self.a, self.b, self.c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
self.attr = value
>>> obj = MyClass(1,2,3)
>>> obj.setattr_(a,4) # obj.a = 4
AttributeError: 'obj' is read-only # __setattr__ method also applies internally
This is a use case for properties. Properties without a setter are read-only. In the following, a and b are read-only, while c is not.
class MyClass:
def __init__(self, a, b, c):
self._a = a
self.b = b
self._c = c
# a is a read-only property
#property
def a(self):
return self._a
# b is an ordinary attribute
# c is a property you can set
#property
def c(self):
return self._c
#c.setter
def c(self, value):
self._c = value
Since you have defined only getters for the a, attempts to
change its value will fail. Attempts to change b will succeed as expected. Attempts to change c will succeed as
if it were a regular attribute.
>>> obj = MyClass(1,2,3)
>>> obj.a = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> obj.b = 5
>>> obj.c = 6
>>> obj.c
6
You can use properties in Python for this type of tasks. First, you make your attribute 'private' by adding two underscores, then you create a getter method with the #property decorator:
class MyClass:
def __init__(self, a, b, c):
self.__a, self.__b, self.__c = a, b, c
#property
def a(self):
return self.__a
#property
def b(self):
return self.__b
#property
def c(self):
return self.__c
Now, you can use your class like this:
>>> my_object = MyClass('foo', 'bar', 'bar')
>>> print(my_object.b)
bar
>>> my_object.b = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
Note
I wrote 'private' because you can still access it if you really want:
>>> my_object._MyClass__b = 42
>>> print(my_object.b)
42
This has to do with the Zen of Python: "We’re all consenting adults here".
Please use the properties.
Anyway, it is good to understand the internals, here is a working code based on your question. Just to play with.
Once you redefine __setattr__ to fail, there is no way to set an attribute in that class. But there is still a working __setattr__ left in the parent class.
class MyClass():
def __init__(self, a, b, c):
self.setattr_('a', a)
self.setattr_('b', b)
self.setattr_('c', c)
def __setattr__(self,attr,value):
raise AttributeError('%r is read-only' % self)
def setattr_(self,attr,value):
super().__setattr__(attr, value)
obj = MyClass(1,2,3)
obj.setattr_('a',4) # note that a is a name (string)

Dependent properties that are CPU intensive

I am very often confronted to this case and did not manage to discover a stable way to deal with it.
Suppose I have a class defined like that:
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
#property
def c(self):
"""This method performs some heavy computations based on a and b properties"""
# Some heavy computations only with a and b attributes
return c
Property c may be now be retrieved by:
>>> obj = MyClass(a, b)
>>> print obj.c
However, every time I ask for obj.c, the heavy computations will be performed, resulting in a poor performance code as c results from heavy computations and it would preferably be calculated only while a or b is set or modified.
What would the better way to deal with this case ? I am thinking of creating a c_update method to use as a decorator for some #a.setter and #b.setter decorated methods but is that the better way ?
Regards,
But what if I have a lot of XX dependent properties that rely on a and b values. Do I have to write an update_XX method for each of them and add this method to init and to each a.setter and b.setter ? That seems to me quite verbose...
You can have the c value (and any other number of dependent properties) updated everytime either a or b is mutated, I implemented an update_properties() method below:
class MyClass(object):
def __init__(self, a, b):
self._a = a
self._b = b
self.update_properties()
#property
def a(self):
return self.a
#a.setter
def a(self, value):
self._a = value
self.update_properties()
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
self.update_properties()
def update_properties(self):
self.c = self._a + self._b
self.d = self._a * self._b
self.e = self._a - self._b
# self.f = ...
# ...
# self.z = ...
# Can go on as long as you want
Do you think it would be possible to implement this machinery as some decorators in order to lighten the code
The verbosity seems to be only on the side that tracks the free variables (e.g. here a and b), so if I had to support an arbitrary number of those, I would implement a MyClass.set_value(name, value)
def set_value(self, name, value):
setattr(self, name, value)
self.update_properties()
So the idea here is that our set_value() can work with an arbitrary number of attributes. And it's possible to call it from __init__ if you use **kwargs to unpack the key-values passed to the constructor.
One requirement here, since we haven't set the free variables as #property we're required to use obj.set_value('a', 42) instead of obj.a = 42
There is a small pypi package that fits well: cached-property
from cached_property import cached_property
class MyClass(object):
def __init__(self):
pass
#cached_property
def c(self):
# Heavy computation based on self.a / self.b
return ...
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
del self.c
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b = value
del self.c
Of course you could also build an abstraction for the a/b properties on top of that, that utilizes del self.c.
One benefit of using cached_property is that you can easily make the cache thread-safe by changing it to threaded_cached_property.
I'd simply store the actual value of c in a private attribute and check if this is not None. Set this to None when either a or b changes.
So the "proper" way of doing this using properties would be:
class MyClass(object):
def __init__(self, a, b):
self._a = a
self._b = b
self._c = None
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
self._c = None
#property
def b(self):
return self._b
#b.setter
def a(self, value):
self._b = value
self._c = None
#property
def c(self):
if self._c is None:
self._c = # compute c here
return self._c
If you want to avoid creating all these properties and setters you probably want to hijack the __getattr__ and __setattr__ methods instead:
class MyClass(object):
def __init__(self, a, b):
self.a = a
self.b = b
self._c = None
def __getattr__(self, name):
if name == 'c':
if self._c is None:
self._c = # compute c here
return self._c
raise AttributeError(name)
def __setattr__(self, name, value):
if name == 'c':
raise TypeError('You cannot modify property c directly')
super(MyClass, self).__setattr__(name, value)
if name in ('a', 'b'):
super(MyClass, self).__setattr__('_c', None)
Note that this last solution could be extended to like 10 attributes a1, ..., a10 without having to define 10 properties and setters.
It's probably a bit less robust.
So based on your answers, I managed to build a new answer using a dict for dependent properties.
class MyClass(object):
def __init__(self, a, b):
self._properties = dict()
self._a = a
self._b = b
def _update_dependent_properties(self):
# Do computations for c1, c2...
self._properties['c1'] = # c1 value
self._properties['c2'] = # c2 value
# ...
#property
def a(self):
return self._a
#property
def b(self):
return self._b
#a.setter
def a(self, value):
self._properties.clean()
self._a = value
#b.setter
def b(self, value):
self._properties.clean()
self._b = value
#property
def c1(self):
try:
return self._properties['c1']
except KeyError:
_update_dependent_properties()
return self._properties['c1']
#property
def c2(self):
try:
return self._properties['c2']
except KeyError:
_update_dependent_properties()
return self._properties['c2']
This seem to do the trick but it is still quite verbose... and I have still to write a property for each of the dependent property I am expecting. However, it does force the calculation of update_dependent_properties() when either attribute a or b is modidied.
I wonder if it does not exist a module to do it. It seem that my problem sounds like memoize technique... and may a decorator lighten the code by systematizing the procedure ?

Python #property decorator not working

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.

Categories