Reputation: 200
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
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