Search code examples
pythonpython-3.xsetattr

How to call __setattr__() correctly in Python3 as part of a class?


I want to call a function when an object attribute is set:

class MyClass():
    myattrib = None

    def __setattr__(self, prop, val):
        self.myattrib = val
        print("setting myattrib")

x = MyClass()
x.myattrib = "something"

The problem is, that this creates an infinite recursion.

The idea is to call a function when a member variable is set (and actually set that member variable), but at the same time run extra code (showcased with the print() statement in the example above).

The other obvious way to do this, is using set_attrib() functions, as is common in Java, but I'd like to do it the pythonic way, and set the attributes directly, but at the same time running extra code.


Solution

  • Call the base version via super():

    class MyClass(object):
        myattrib = None
    
        def __setattr__(self, prop, val):
            super().__setattr__('myattrib', val)
            print("setting myattrib")
    

    You probably do not want to ignore the prop argument here, it is not necessarily 'myattrib' that is being set.

    However, consider using a property instead of intercepting all attribute setting:

    class MyClass(object):
        _myattrib = None
    
        @property:
        def myattrib(self):
            return self._myattrib
    
        @myattrib.setter
        def myattrib(self, val):
            self._myattrib = val
            print("setting myattrib")
    

    I added object as a base-class; this is the default in Python 3, but a requirement for super() and property objects to work in Python 2.