-1

Why is the presence of super().__init__() in the base classes necessary for the super().__init__() in the child class to run through both inheritance branches?

The code below illustrates what happens with and without super().__init__() in the base classes:

import sys
#--------------------------------
class one():
  def __init__(self):
    super().__init__()
    print("one")


class two():
  def __init__(self):
    super().__init__()
    print("two")

class three(one,two):
  def __init__(self):
    super().__init__()
    print("three") 
#--------------------------------

class uno():
  def __init__(self):
    print("uno")

class dos():
  def __init__(self):
    print("dos")

class tres(uno,dos):
  def __init__(self):
    super().__init__()
    print("tres") 
    
print("python version")
print(sys.version)
print("---------------")
a = three()
print("---------------")
b = tres()

Output:

python version
3.8.12 (default, Aug 30 2021, 16:42:10) 
[GCC 10.3.0]
---------------
two
one
three
---------------
uno
tres
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
CABrouwers
  • 33
  • 2

2 Answers2

0

super() is misnamed in the multiple-inheritance case. Every class has a list of classes called the "method resolution order". For single inheritance, it is precisely each class followed by its superclass. For multiple inheritance, it is more complicated.

super() says to call the next implementation in the object's method resolution order. For this class, the mro is tres, then uno, then dos, then object.

Google "Method resolution order" for more info.

Frank Yellin
  • 9,127
  • 1
  • 12
  • 22
  • Thanks, I had read many explanations of MRO, but what I didn't get from those explanations was that the MRO determines not only the symbol resolution order for a method but also the recursion order of a call to super(). Anyway, I think I am going to stay away from using super() as much as possible; I don't like the idea that the resolution of a symbol within a cursive chain depends on where the recursion was initiated from. – CABrouwers May 05 '22 at 04:13
0

Usually in multiple inheritance, if the two parent classes have the same definition (in this case __init__), the child calls the first parent (in this case its one). However, inside one's __init__ you call another super, but you will notice that one has no parent. In this case python calls the second parent's __init__.

Try removing the super from inside two. You will have the same output. Now undo the deleted super, delete the super from class one, and change the order of inheritance:

class three(two,one):

You will have three calling two which then calls one; therefore outputting "one two three". Multiple inheritance and multi-level inheritance have some special cases like that, and I agree with Frank Yellin that it is better for you to just read about MRO.

Mohamed Yasser
  • 641
  • 7
  • 17
  • Thanks for your response. All explanations of MRO focus on describing the method resolution order within the lineage of a class. My question was about the recursion order of a call to super(). I realize now that the recursion order is also the MRO. However, I don't think it is self-evident; symbol resolution and recursion are distinct things. MRO is unsettling because it breaks the OOP principle of encapsulation. A recursive call to super() from a parent class resolves to a different predecessor depending on which child started the recursion in the first place. – CABrouwers May 05 '22 at 03:51