Understanding @property decorator and inheritance
Question: Python 3 here, just in case it's important.

I'm trying to properly understand how to implement inheritance when
`@property` is used, and I've already searched StackOverflow and read like 20
similar questions, to no avail because the problems they are trying to solve
are subtly different. This is the code I'm using for testing:

    
    
    class Example:
        def __init__(self):
            self.__data = None
    
        @property
        def data(self):
            return self.__data
    
        @data.setter
        def data(self, data):
            self.__data = data
    
    
    class Example2(Example):
        def __init__(self):
            super().__init__()
    
        @property
        def data(self):
            return super().data  # Works!
    
        @data.setter
        def data(self, data):
            data = '2' + data
            #Example.data = data   # Works, but I want to avoid using the parent name explicitly
            #super().data = data  # Raises AttributeError: 'super' object has no attribute 'data'
            #super().data.fset(self, data) # Raises AttributeError: 'NoneType' object has no attribute 'fset'
            #super(self.__class__, self.__class__).data = data  # Raises AttributeError: 'super' object has no attribute 'data'
            super(self.__class__, self.__class__).data.fset(self, data)  # Works!
    
    
    a = Example2()
    a.data = 'element a'
    print(a.data)
    

What I can't understand is why `super().data` works in `Example2` getter, but
not in setter. I mean, why in the setter a class bound method is needed, but
in the getter an instance bound method works?

Could anyone please point me to an explanation or explain why I'm getting
`AttributeError` in three of the five different calls I'm testing?

Yes, I know, I could use `Example.data` in the setter, but that's not needed
in the getter and a) I would prefer not to use the parent class name
explicitly if possible and b) I don't understand the asymmetry between getter
and setter.


Answer: you should do something like this:

    
    
    class Example:
        def __init__(self):
            self._data = None
    
        @property
        def data(self):
            return self._data
    
        @data.setter
        def data(self, data):
            self._data = data
    
    
    class Example2(Example):
        def __init__(self):
            super().__init__()
    
        @Example.data.setter
        def data(self, data):
            data = '2' + data
            self._data = data
    
    
        a = Example2()
        a.data = 'element a'
        print(a.data)
    

you are getting the Attribute error because the class does not have the data
Attribute, the instance has it.

If you want to override the @property, just do it:

    
    
    class Example:
    def __init__(self):
        self._data = None
    
        @property
        def data(self):
            return self._data
    
        @data.setter
        def data(self, data):
            self._data = data
    
    
    class Example2(Example):
        def __init__(self):
        super().__init__()
    
        @property
        def data(self):
            return self._data
    
        @data.setter
        def data(self, data):
            data = '2' + data
            self._data = data
    

