Use parent property setter when overriding in child class
Asked Answered
M

2

6

I have a situation where I am overriding just the setter on a property:

class Parent:
    @property
    def x(self):
        return self.__dict__['x']
    @x.setter
    def x(self, val):
        self.__dict__['x'] = f'P{val}'

class Child(Parent):
    @Parent.x.setter
    def x(self, val):
         super().x = val
         print('all set')

Here, the print statement represents the processing I want to do after invoking the parent's setter. You can just ignore it. super().x = y is my native attempt at invoking said setter. It fails with

Attribute error: 'super' object has no attribute 'x'

What is the correct way to use the property setter from the parent class in the child?

I'd prefer to avoid the following, even though it works just fine, since it involves explicitly calling dunder methods:

Parent.x.__set__(self, val)

As a bonus, is there any way to use super() in the body of the child's setter?

Makeup answered 16/8, 2018 at 14:46 Comment(4)
One quick way out of this is to define a separate method which the setter actually calls, and then subclasses override that method.Electrokinetics
You could also just manually call the setter out of the parent property, but that’s probably not as understandable as metatoaster’s solution, and won’t automatically handle multiple inheritance the way super does, etc.Sanction
Why not super().__dict__['x'] = val?Kieger
@khachik. The whole point is to avoid re-implementing the parent setter.Makeup
A
3

You can use the property's fset function:

class Child(Parent):
    @Parent.x.setter
    def x(self, val):
        Parent.x.fset(self, val)
        print('all set')
Abie answered 16/8, 2018 at 15:36 Comment(0)
H
1

I posted a very similar question.

After a correction to a mistake of mine(by @ShadowRanger) the answer I preferred most was:

class Child(Parent):
    @Parent.x.setter
    def x(self, val):
         super(__class__, type(self)).x.fset(self, val)
         print('all set')

However, I absolutely agree that super().x = val would be a much more natural construct, but would need resolving by the python compiler.

Hullabaloo answered 20/1 at 12:20 Comment(3)
This is basically the same as Martin's answer, with the replacement of Parent by super(). You likely don't need __class__, type(self) here at all.Makeup
You do need the __classs__, type(self) and using super() resolves the type(self).__mro__ to the next ancestral definition (you don't need apriori knowledge where the property is defined).Hullabaloo
Sorry, apologies I still have @Parent.x.setter as a decorator. That's not going to work in the general case that the property x is somewhere up the __mro__ chain.Hullabaloo

© 2022 - 2024 — McMap. All rights reserved.