How Python's Super() works in multiple inheritance for superclasses?

Question:

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.

Asked By: Flammy

||

Answers:

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).

Answered By: Anentropic

does object class also have __init__() method in them?

Answered By: Sandeep Chetia