I'm trying to understand how super works in python's multiple inheritance for the superclasses, for example in the code below:
class First():
def __init__(self, parm1, **kwargs):
super().__init__(**kwargs)
self.parm1 = parm1
self.parm3 = 'one'
class Second():
def __init__(self, parm2 = 'zero', **kwargs):
super().__init__(**kwargs)
self.parm2 = parm2
class Third(First,Second):
def __init__(self,parm1):
super().__init__(parm1=parm1)
trd = Third('tst')
print(trd.parm1) # 'tst'
print(trd.parm3) # 'one'
print(trd.parm2) # 'zero'
If I remove the super().__init__(**kwargs)
the execution ends with
'Third' object has no attribute 'parm2'
printing only the parm1
and parm3
, even if I declared the hierarchy in Class Third(First,Second)
.
I know all classes inherit from Object class, but I don't understand how it could be involved with the super() class in the parent classes and how the latter allows to access to the second parent's attributes.
CodePudding user response:
See https://realpython.com/lessons/multiple-inheritance-python/
Base classes are applied from left onto right, i.e. the most base class is on the right.
e.g. in class Third(First,Second)
things from First
will override things from Second
. i.e. Second
is the most base class here. (Judging by the naming, you probably expected the reverse?)
We can also see this by inspecting the "Method Resolution Order" of Third
:
In [1]: Third.__mro__
Out[1]: (__main__.Third, __main__.First, __main__.Second, object)
We can read this tuple as child->parent in left-to-right order. See https://stackoverflow.com/a/2010732/202168
Another thing to know is that super().__init__()
will find a different parent class to call depending on the context.
For example First
does not inherit from Second
, so if you instantiate a First
the super
call will go straight to object
. We can see this in the MRO:
In [2]: First.__mro__
Out[2]: (__main__.First, object)
But when we instantiate a Third
, that class inherits from both First
and Second
... so the super
call chain will reflect the order in Third.__mro__
... meaning that the super
call in First
will now call the method in Second
(because Second
is the next parent class from the perspective of the child class that initiated the super
call chain). And the super
call in Second
will call the __init__
of object
, where the chain ends.
All this means that if you remove super().__init__(**kwargs)
from First
then the chain of super calls is broken before reaching Second.__init__
.
Hence you get the AttributeError
"'Third' object has no attribute 'parm2'" when you try to print(trd.parm2)
.