In a real world program I have ran into the next problem: I have a diamond inheritance having SuperClass, MidClassA, MidClassB and SubClass. SuperClass has a property (a filename, actually) that is used by its successors, but different ways (with or without extension). At all levels I want to set this property by a setter. Here is an example code:
class SuperClass:
def __init__(self):
print("SuperClass __init__")
super().__init__()
@property
def value(self):
return self._value
@value.setter
def value(self, new_value):
self._value = new_value
print("Superclass setter called!")
class MidClassA(SuperClass):
def __init__(self):
print("MidClassA __init__")
super().__init__()
@SuperClass.value.setter
def value(self, new_value):
super(MidClassA, MidClassA).value.__set__(self, new_value)
print("MidClassA setter called!", MidClassA.__mro__)
class MidClassB(SuperClass):
def __init__(self):
print("MidClassB __init__")
super().__init__()
@SuperClass.value.setter
def value(self, new_value):
super(MidClassB, MidClassB).value.__set__(self, new_value)
print("MidClassB setter called!", MidClassB.__mro__)
class SubClass(MidClassB, MidClassA):
def __init__(self):
print("SubClass __init__")
super().__init__()
@SuperClass.value.setter
def value(self, new_value):
super(SubClass, SubClass).value.__set__(self, new_value)
print("Subclass setter called!", SubClass.__mro__)
obj = SubClass()
obj.value = 42
print(obj.value)
And an ouput:
SubClass __init__
MidClassB __init__
MidClassA __init__
SuperClass __init__
Superclass setter called!
MidClassB setter called! (<class '__main__.MidClassB'>, <class '__main__.SuperClass'>, <class 'object'>)
Subclass setter called! (<class '__main__.SubClass'>, <class '__main__.MidClassB'>, <class '__main__.MidClassA'>, <class '__main__.SuperClass'>, <class 'object'>)
42
Questions:
- As output displays, the MRO of __init__s works well (SubClass-MidClassB-MidClassA-SuperClass-object) but during the propery setting MidClassA is skipped.
- How to get rid of the
super(MidClassB, MidClassB)? - Is it possible to get rid of the
__set__method?
An ideal solution would be something like super().value = new_value, but all these don't work.
Just after posting this I realized that two of the three above mentioned questions has a good solution:
super(MidClassB, MidClassB)is a mistaken expression as it "positions" to the same class in MRO regarding the actual class of the object. So the proper designation issuper(MidClassB, self.__class__). This will return the proper class those set method will be called.The only question left is whether it is possible to have an instance setter instead of this classmethod
__set__(cls, instance, value).So a working piece of code is here:
and an output:
PS as far as I understand
super()is an important and practical, yet heavily underdocumented function in Python. It takes two parameters.super()is called.self(and this is the default value of this parameter), while for class parameters this should beself.__class__but I haven't seen this being mentioned anywhere. Interestingly enough, however, for class methods, like__new__(), this second parameters defaults tocls, so usingsuper()within a class method without parameters is meaningful.