mareoraft
mareoraft

Reputation: 3902

Overriding an inherited property setter

I have a class called Node that has an importance setter and getter, below:

class Node:

    @property
    def importance(self):
        return self._importance

    @importance.setter
    def importance(self, new_importance):
        if new_importance is not None:
            new_importance = check_type_and_clean(new_importance, int)
            assert new_importance >= 1 and new_importance <= 10
        self._importance = new_importance

Later on, I have a class Theorem that inherits from Node. The only difference between a Theorem and a Node, as far as importance is concerned, is that a Theorem must have an importance of at least 3.

How can a Theorem inherit the importance setter, but add on the additional constraint that importance >= 3?

I tried to do it this way:

class Theorem(Node):

    @importance.setter
    def importance(self, new_importance):
        self.importance = new_importance # hoping this would use the super() setter
        assert self.importance >= 3

Upvotes: 18

Views: 10476

Answers (3)

jonrsharpe
jonrsharpe

Reputation: 121976

Here is a completely different solution to the broader problem, with a lot less faffing around:

class Node:

    MIN_IMPORTANCE = 1
    MAX_IMPORTANCE = 10

    @property
    def importance(self):
        return self._importance

    @importance.setter
    def importance(self, new_importance):
        if new_importance is not None:
            new_importance = check_type_and_clean(new_importance, int)
            assert (new_importance >= self.MIN_IMPORTANCE and 
                    new_importance <= self.MAX_IMPORTANCE)
        self._importance = new_importance


class Theorem(Node):

    MIN_IMPORTANCE = 3

    # and that's all it takes!

To my mind, this expresses:

The only difference between a Theorem and a Node, as far as importance is concerned, is that a Theorem must have an importance of at least 3.

a lot more clearly than overriding the property setter does.


Note that assert is generally used for testing and debugging, rather than as part of the general program flow, and certainly not for things that you expect could happen; see e.g. Best practice for Python Assert.

Upvotes: 5

Mahi
Mahi

Reputation: 21883

You can refer to the existing property directly through the Node class, and use the property's setter method to create a new property from it:

class Theorem(Node):
    @Node.importance.setter
    def importance(self, new_importance):
        # You can change the order of these two lines:
        assert new_importance >= 3
        Node.importance.fset(self, new_importance)

This will create a new property into Theorem class that uses the getter method from Node.importance but replaces the setter method with a different one. That's how properties in general work: calling a property's setter returns a new property with a custom setter, which usually just replaces the old property.

You can learn more about how properties work by reading this answer (and the question too).

Upvotes: 28

jonrsharpe
jonrsharpe

Reputation: 121976

One way to do this is by implementing a new property on Theorem using the Node getter, providing a new setter method and calling the Node setter explicitly within it:

class Theorem(Node):

    def _set_importance(self, new):
        Node.importance.fset(self, new)
        assert self.importance >= 3

    importance = property(Node.importance.fget, _set_importance)

As far as I'm aware, this cannot be done with super.


Per this bug report, you could do:

class Theorem(Node):

    def _set_importance(self, new):
        super(Theorem, Theorem).importance.fset(self, new)
        assert self.importance >= 3

    importance = property(Node.importance.fget, _set_importance)

However, this is clearly a bit awkward; the patch to allow super() instead appears to be scheduled for Python 3.5 (due in September 2015).

Upvotes: 4

Related Questions