Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using super() in a property's setter method when using the @property decorator raises an AttributeError

I am a little confused by the behavior when attempting to overwrite a property in a subclass.

The first example sets up two classes, Parent and Child. Parent inherits from object, while Child inherits from Parent. The property a is defined using the property decorator. When child.a's setter method is called, an AttributeError is raised.

In the second example, by using the property() function rather than the decorator, everything works as would be expected.

Can anyone shed some light on why the behavior differs? Also, yes, I know that the __init__ definition in Child is not needed.

Example 1 - Using @property

class Parent(object):
    def __init__(self):
        self._a = 'a'
    @property
    def a(self):
        return self._a
    @a.setter
    def a(self, val):
        self._a = val

class Child(Parent):
    def __init__(self):
        super(Child, self).__init__()
    @property
    def a(self):
        return super(Child, self).a
    @a.setter
    def a(self, val):
        val += 'Child'
        super(Child, self).a = val

p = Parent()
c = Child()
print p.a, c.a
p.a = 'b'
c.a = 'b'
print p.a, c.a

Example 1 return - Raises an attribute error

a a
Traceback (most recent call last):
  File "testsuper.py", line 26, in <module>
    c.a = 'b'
  File "testsuper.py", line 20, in a
    super(Child, self).a = val
AttributeError: 'super' object has no attribute 'a'

Example 2 - Using property()

class Parent(object):
    def __init__(self):
        self._a = 'a'
    def _get_a(self):
        return self._a
    def _set_a(self, val):
        self._a = val
    a = property(_get_a, _set_a)

class Child(Parent):
    def __init__(self):
        super(Child, self).__init__()
    def _get_a(self):
        return super(Child, self)._get_a()
    def _set_a(self, val):
        val = val+'Child'
        super(Child, self)._set_a(val)
    a = property(_get_a, _set_a)

p = Parent()
c = Child()
print p.a, c.a
p.a = 'b'
c.a = 'b'
print p.a, c.a

Example 2 return - Works correctly

a a
b bChild
like image 921
Vorticity Avatar asked Nov 28 '12 00:11

Vorticity


People also ask

What does the @property decorator do?

The @property is a built-in decorator for the property() function in Python. It is used to give "special" functionality to certain methods to make them act as getters, setters, or deleters when we define properties in a class.

What is @property method in Python?

The @property Decorator In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is: property(fget=None, fset=None, fdel=None, doc=None) where, fget is function to get value of the attribute. fset is function to set value of the attribute.

What is the more pythonic way to use getter and setter?

Getters and Setters in python are often used when: We use getters & setters to add validation logic around getting and setting a value. To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.

What is @property in Python Django?

In Python, @property is a built-in decorator that creates and returns a property object. The @property decorator is internally taking the bounded method as an argument and returns a descriptor object.


1 Answers

super() returns a proxy object, not a superclass, and it doesn't support the function __set__().

And you can see more details here Python super and setting parent class property and here http://bugs.python.org/issue14965.

like image 84
iceout Avatar answered Oct 08 '22 03:10

iceout