Ryan
Ryan

Reputation: 345

Do properties support inheritance?

In my code class A has a property, but class B doesn't inherit it. Does @property support inheritance? Or is it my fault?

class A(object):

    def __init__(self):
        self._x = 100

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, v):
        self._x = v


class B(A):

    @x.setter
    def x(self, v):
        self._x = v

The error message is as below:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    class B(A):
  File "test.py", line 10, in B
    @x.setter
NameError: name 'x' is not defined

Upvotes: 4

Views: 4635

Answers (2)

jonrsharpe
jonrsharpe

Reputation: 121976

The NameError is because x isn't in the global scope; it's defined within A's class namespace, so you need to access it there explicitly by using A.x. This is a pretty common mistake, see e.g. python subclass access to class variable of parent.

With properties, though, it can get slightly more complex. If you add a new setter in B, then there is no problem using A.x.setter as shown in Dean's answer. However, if you override an existing setter, you will also alter A's behaviour in doing so. I doubt this is the behaviour that you want.

Instead, in this case, you need to create a new property in the subclass with A's getter and a new setter. I think that this is this easiest way to achieve that, with minimal repetition:

class B(A):

    x = property(A.x.__get__)  # new property with same getter

    @x.setter  # new setter on new property
    def x(self, v):
        ...

Note the use of property without the @ syntactic sugar, as I'm not using it to decorate a new method. If you want to access A's setter from B's setter, you can do so with super().x.__set__(whatever).

Upvotes: 6

Dean Fenster
Dean Fenster

Reputation: 2395

It just doesn't know where to get x from. You can do this instead:

class A(object):
    def __init__(self):
        self._x = 100

    @property
    def x(self):
        return self._x

class B(A):
    @A.x.setter
    def x(self, v):
        self._x = v

Upvotes: 3

Related Questions