Search code examples
pythonpython-3.xsettergetterpython-decorators

@property setter not called in python 3


I have a big issue, I'm new with @property and setter but I need to use them for a University Assignment: my problem is that the setters are always bypassed by the getters and also, when i try to pass an argument to the setter, it doesn't work:

class DumbClass():
    def __init__(self, classParam):
        self.ciao = classParam

    @property
    def getCiao(self):
        return self.ciao

    @getCiao.setter
    def setCiao(self,dummy):
        self.ciao = dummy

Then, when i call it I have either:

pizzo = DumbClass('getter')
pozza = pizzo.getCiao('setter')
print(pizzo.ciao, pozza)

 ----------------------------------------------------------------------
 TypeError Traceback (most recent call last)
 <ipython-input-18-78a6b4607757> in < module>()

       1 pizzo = DumbClass('getter')
 ----> 2 pozza = pizzo.getCiao('setter')
       3 print(pizzo.ciao, pozza)

 TypeError: 'str' object is not callable

Or, if I don't pass any arguments:

pizzo = DumbClass('getter')
pozza = pizzo.getCiao
print(pizzo.ciao, pozza)

----------------------------------------------------------------------
getter getter

I can I make the setter to be called?


Solution

  • The setter is only invoked when you try to assign to the property. In the line

    pozza = pizzo.getCiao('setter')
    

    you first access the value of the property with pizzo.getCiao (which calls the getter and returns the string "getter", then attempts to call the string as a function with the argument 'setter'.

    You have basially created a read-only property:

    >>> pizzo = DumbClass('getter')
    >>> pizzo.getCiao
    'getter'
    >>> pizzo.getCiao = 'foo'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute
    

    This is because you didn't use the same name when defining both the getter and setter, so your property doesn't have a setter. (Arguably, getCiao.setter should probably raise an error right away, since the name matters, but it doesn't. C'est la vie.)

    So the right thing to do is use the same name for both. The usual convention is to use an private variable with the "same" name as the property to store the underlying data for simple properties like this.

    class DumbClass:
        def __init__(self, p):
            self._ciao = p
    
        @property
        def ciao(self):
            return self._ciao
    
        @ciao.setter
        def ciao(self, v):
            self._ciao = v
    

    Now you should be able to both get and set the value of the propery.

    >>> d = DumbClass("hi")
    >>> d.ciao
    'hi'
    >>> d.ciao = 'bye'
    >>> d.ciao
    'bye'