Vincent
Vincent

Reputation: 200

How to properly extend a setter in python?

I'm looking for a way to extend the setter of a property when subclassing a python class. I looked there and there, but the answers provided either seem to be complicated, or do not cover the case of extending the setter instead of overriding it. Since these questions are rather old, I thought maybe there was a better way to override a setter.

Looking here in the documentation, I found out that properties have fget, fset and fdel attributes refering to getter, setter and deleter methods. Using that, I came up with the following solution.

class Parent:
    def __init__(self, x):
        self.x = x

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

    @x.setter
    def x(self, new_x):
        print('This is Parent\'s setter')
        self._x = new_x


class Child(Parent):
    @Parent.x.setter
    def x(self, new_x):
        Parent.x.fset(self, new_x)
        print('This is Child\'s setter')


test = Child(0)

Running the script, I get the output

This is Parent's setter
This is Child's setter

which is exactly what I expect.

This solution seems to work, but I feel like there should be a better solution, since it seems weird to hard-code the name of the parent class in the setter of the child class, and to explicitly use self as an argument. Is there a better way to do that? For example, is there a way to do something similar using super? What is the standard way to extend a setter in a subclass?

Upvotes: 0

Views: 326

Answers (1)

user2357112
user2357112

Reputation: 280291

If you want to use super, it'd look like this:

class Child(Parent):
    @property
    def x(self):
        return super().x
    @x.setter
    def x(self, new_x):
        super(Child, type(self)).x.fset(self, new_x)
        print("This is Child's setter")

super implements a __getattribute__ method, but no __setattr__, so we can't do super().x = new_x. We can use super to look up the parent's property object, but in the setter, that's all it can do for us.

There is very little benefit to doing things this way. It may be useful in certain multiple inheritance situations, if two subclasses want to cooperatively override the same property, but doing multiple inheritance correctly is much more complicated than just using super everywhere.

(Passing type(self) to super is almost always wrong, but that's when you're passing it as the first argument. super(type(self), self) breaks in the presence of grandchild classes. Passing type(self) as the second argument makes sense if you want to use super to look up a class attribute in an instance method, which is what we're doing here.)

Upvotes: 1

Related Questions