Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to initialize attributes calculated from other attributes in python classes

I have an attribute in a class that I want to calculate from other attributes.

I want this attribute to be private, so I would like to make it a property.

I understand that a property still needs to be called in the init block of the class, however i didn't find an elegant way to write that.

Some ugly code that does what I need:

class ABC:
    def __init__(self, t=0):
        self.attrA = t
        self._attrB = None

    @property
    def attrB(self):
        return self._attrB

    @attrB.setter
    def attrB(self, value):
        self._attrB = 2*self.attrA

obj = ABC(10)
print(obj.attrB)

Question: how can I avoid declaring attrB in init with a dummy 'None' value?

Edit to previous post:

  • Ideally I would like attrB to really be a 'stored' attribute (not just a property getter), so that it is not recalculated every time it is called: in my usecase attrA and attrB are long lists so recalculating attrB each time it is called is a no-go

  • I understand caching the property call is also an option, but is there not a simpler way by having a real attribute?

like image 336
crixus Avatar asked Oct 29 '25 08:10

crixus


1 Answers

You don't need a self._attrB at all, or an attrB.setter, since your attrB getter can return whatever you want the value of attrB to be (in this case it's twice the value of attrA):

class ABC:
    def __init__(self, t=0):
        self.attrA = t

    @property
    def attrB(self):
        return 2 * self.attrA

obj = ABC(10)
print(obj.attrB)

A good clue that your attrB setter was unnecessary is that it ignored the value that you passed to it! It's simpler to just put this logic in the getter since it's not dependent on any data that isn't already stored in the class.

(edit) If the logic you use to generate attrB is more complicated, you could cache it like this:

from functools import lru_cache

class ABC:
    def __init__(self, t=0):
        self.attrA = t

    @property
    def attrB(self):
        return self._attrB(self.attrA)

    @lru_cache
    def _attrB(self, attrA):
        return 2 * self.attrA

The @lru_cache will cache the return value of _attrB based on the value of attrA, so it will recompute if attrA has changed, and otherwise serve up the value from the cache.

If you don't want attrB to change when attrA changes, then you can simply use cached_property, which computes the value the first time you access it and thereafter uses a cached copy:

from functools import cached_property

class ABC:
    def __init__(self, t=0):
        self.attrA = t

    @cached_property
    def attrB(self):
        return 2 * self.attrA

and obviously if you don't want attrA itself to change after it's initialized, you can make it a private variable.

like image 98
Samwise Avatar answered Oct 31 '25 02:10

Samwise



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!