3

For a Python novice, please can someone tell me what is happening here? Why is it printing "A init"?

If i remove the line super().__init__() from __init__ function of class B then the behaviour is as expected. But why is below code not an error, I though B would not have a superclass??

class A:

    def __init__(self):
        print("A init")

class B:

    def __init__(self):
        super().__init__()   # why is this line not an error
        print("B init")

class C(B, A):

    def __init__(self):
        super().__init__()
        print("C init")

c = C()

Output

A init

B init

C init

Process finished with exit code 0
Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
zeitgeist
  • 39
  • 2
  • 4
    in python 3 all classes inherit from `object`. – David Zemens Jun 24 '19 at 02:04
  • 1
    I see. But when i call the constructor of C then why is it printing "A init", I was thinking as per MRO rules it would only print "B init" as it resolved to the "left most class"? It seems the init function of B is calling the init function of A. – zeitgeist Jun 24 '19 at 02:07
  • i think C is calling both A and B, multiple inheritance – David Zemens Jun 24 '19 at 02:08
  • Hmm. As per my understanding, C should call its own init function, if defined., and in my code when I call init of super() in init of C then it should call init of B as B is the "superclass" by precedence (left-most) as it C is defined as C(B,A) – zeitgeist Jun 24 '19 at 02:10
  • You're not wrong on either point. C does call its own, and B calls A in this case. `b = B()` will *not* call A. gilch's answer seems to give the reasoning per MRO. Cheers – David Zemens Jun 24 '19 at 02:24
  • Thanks a lot David! I understand now. – zeitgeist Jun 24 '19 at 02:25
  • 1
    BTW you can use this online tool to visualize code execution http://www.pythontutor.com/visualize.html#mode=edit – David Zemens Jun 24 '19 at 02:33

1 Answers1

4

You create an instance of C, which we'll call self. It has an MRO of C, B, A, object.

C's __init__ first calls super().__init__(). This delegates to the __init__ in the next class in the MRO, which is B, using the same self object.

B's __init__ first calls super().__init__(). This delegates to the __init__ in the next class in the MRO of self's class (C), which is A.

A's __init__ prints and returns to B's __init__.

B's __init__ prints and returns to C's __init__.

C's __init__ prints and returns to the constructor (object's __new__), which returns self.

super() has two hidden arguments, __class__ (so it knows where to start from in the MRO), and self (or cls, for @classmethods—it's whatever the first argument is, regardless of name).

You had to provide these explicitly in Python 2 (and still can), but in Python 3 super() actually inspects the stack frame of its caller and finds these when called from inside a method. (The __class__ variable is implicitly available inside methods, and is the class it was defined in.)

gilch
  • 10,813
  • 1
  • 23
  • 28