In class object, how to auto update attributes?

39,637

Solution 1

if updating one property due to an update on another property is what you're looking for (instead of recomputing the value of the downstream property on access) use property setters:

class SomeClass(object):
    def __init__(self, n):
        self.list = range(0, n)

    @property
    def list(self):
        return self._list
    @list.setter
    def list(self, val):
        self._list = val
        self._listsquare = [x**2 for x in self._list ]

    @property
    def listsquare(self):
        return self._listsquare
    @listsquare.setter
    def listsquare(self, val):
        self.list = [int(pow(x, 0.5)) for x in val]

>>> c = SomeClass(5)
>>> c.listsquare
[0, 1, 4, 9, 16]
>>> c.list
[0, 1, 2, 3, 4]
>>> c.list = range(0,6)
>>> c.list
[0, 1, 2, 3, 4, 5]
>>> c.listsquare
[0, 1, 4, 9, 16, 25]
>>> c.listsquare = [x**2 for x in range(0,10)]
>>> c.list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Solution 2

Absolutely. But use a property instead.

class SomeClass(object):
  def __init__(self, n=5):
    self.mylist = range(n)

  @property
  def listsquare(self):
    return [ x**2 for x in self.mylist ]

a = SomeClass()
a.mylist = [4, 5, 8]
print a.listsquare

Caching of the property value is left as an exercise for the reader.

Solution 3

Ignacio's @property solution is great but it recalculates the list every time you reference listsquare - that could get expensive. Mathew's solution is great, but now you have function calls. You can combine these with the 'property' function. Here I define a getter and a setter for my_list (I just couldn't call it 'list'!) that generates listsquare:

class SomeClass(object):

    def __init__(self, n=5):
        self.my_list = range(n)

    def get_my_list(self):
        return self._my_list

    def set_my_list(self, val):
        self._my_list = val
        # generate listsquare when my_list is updated
        self.my_listsquare = [x**2 for x in self._my_list]

    # now my_list can be used as a variable
    my_list = property(get_my_list, set_my_list, None, 'this list is squared')

x = SomeClass(3)
print x.my_list, x.my_listsquare
x.my_list = range(10)
print x.my_list, x.my_listsquare

This outputs:

[0, 1, 2] [0, 1, 4]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Solution 4

You can also just use setter methods, like this:

class SomeClass:
    def __init__(self, n=5):
        self.set_list(range(n))

    def set_list(self, n):
        self.list = n
        self.listsquare = [ x**2 for x in self.list ]

b = SomeClass()
b.set_list(range(5,10))
Share:
39,637
LWZ
Author by

LWZ

I'm learning Python.

Updated on September 04, 2020

Comments

  • LWZ
    LWZ over 3 years

    I have a class which has multiple attributes that are related, for example:

    class SomeClass:
        def __init__(self, n=0):
            self.list = range(n)
            self.listsquare = [ x**2 for x in self.list ]
    

    If I make an object normally that would no problem, with

    a = SomeClass(10)
    

    I will get 2 lists, a.list and a.listsquare.

    Now if I want to make a empty object first, and assign one attribute to it, I want the other attributes to be automatically updated, for example if I do

    b = SomeClass()
    b.list = range(5,10)
    

    I want b.listsquare to be automatically updated, and also the other way around (assign b.listsquare and auto update b.list). Is this possible? Is Class the right choice for this?


    Thanks to you all, but I'm completely overwhelmed by all the different answers. Can anyone give a complete solution so I can learn write my own?

    I would like to achieve a class Foo with 3 attributes length, list and listsquare such that:

    1. If I do a = Foo(3), I get a.length = 3, a.list = [0, 1, 2], a.listsquare = [0, 1, 4].
    2. If I do b = Foo().list = [5, 6], I get b.length = 2, b.listsquare = [25, 36].
    3. If I do c = Foo().listsquare = [4, 9], I get c.length = 2, c.list = [2, 3].
  • LWZ
    LWZ about 11 years
    Great! What about the other way around? Can I define a.listsquare and get a.mylist?
  • Ignacio Vazquez-Abrams
    Ignacio Vazquez-Abrams about 11 years
    Yes. Just add a setter to your property.
  • LWZ
    LWZ about 11 years
    Does this mean I need to define 3 properties (size, list, listsquare) so when I update one of them the other 2 will update?
  • Ali-Akber Saifee
    Ali-Akber Saifee about 11 years
    that is correct. i've updated the above answer to work the way you wanted - the size attribute feel necessary anymore.
  • tdelaney
    tdelaney about 11 years
    thanks to the folks pointing out i had initialized the list incorrectly.