1

I'm confused about the way Python class inherit from multiple parent classes.

If the parent classes all inherit from the same grand-parent class, everything is wonderful.

# grand-parent class
class Z():
    def __init__(self):
        pass

# parent class A
class A(Z):
    def __init__(self):
        super().__init__()
        self.x = 1

# parent class B
class B(Z):
    def __init__(self):
        super().__init__()
        self.y = 2

# parent class C
class C(Z):
    def __init__(self):
        super().__init__()
        self.z = 3

# target class D
class D(A, B, C):
    def __init__(self):
        super().__init__()

d = D()
print(vars(d))
#{'x': 1, 'y': 2, 'z': 3}

Without the same grand-parent class, only variables from the first parent class is inherited.

# parent class A
class A():
    def __init__(self):
        self.x = 1

# parent class B
class B():
    def __init__(self):
        self.y = 2

# parent class C
class C():
    def __init__(self):
        self.z = 3

# target class D
class D(A, B, C):
    def __init__(self):
        super().__init__()

d = D()
print(vars(d))
#{'x': 1}
Gary Liao
  • 61
  • 5
  • 1
    You haven't just removed the common ancestor, you've removed *the super calls*. Also you've mistyped `__init` in both examples. – jonrsharpe Oct 11 '19 at 07:33
  • I fixed the typo error. I don't understand the rest of your comment. – Gary Liao Oct 11 '19 at 07:39
  • 1
    What exactly don't you understand? I literally just mean you've removed the calls to super, in the parent class implementations of `__init__`. If you put those back, it will work whether or not Z is involved. – jonrsharpe Oct 11 '19 at 07:41
  • You are right, but I don't understand why. Why class A, B, C can call super().__init__() while they don't inherit from any class? – Gary Liao Oct 11 '19 at 07:44
  • 1
    They inherit from object. – jonrsharpe Oct 11 '19 at 07:46
  • Great thanks. It seems that I asked a stupid question, but I read books and search on Google for several hours, just couldn't find the right path to this simple and clear answer. should I delete this question? – Gary Liao Oct 11 '19 at 07:50
  • 1
    @jonrsharpe, Could you elaborate a bit more? I checked the mro of the class D (the second implementation in the post) and got this `>>> D.mro() [, , , , ]`. It does inherit from class A, B and C ` So why doesn't it have the attributes from these classes? Do you have any links that mandate having `super` being called in parent classes so that child classes can inherit their variables? – Clock Slave Oct 11 '19 at 07:52
  • @ClockSlave what? It *does* inherit their attributes. `__init__`, like other methods, is an attribute. However, there are multiple implementations of that method - B's is *shadowed* by A's, for example. That's why you need super. – jonrsharpe Oct 11 '19 at 08:04
  • @jonrsharpe Okay. I understand there are multiple, `__init__` implementations with that of `A` taking the highest precedence. What I am trying to understand is how does the adding of `super` to A, B, and C change the outputs that the OP has shown? – Clock Slave Oct 11 '19 at 08:13
  • @ClockSlave see e.g. https://stackoverflow.com/questions/222877/what-does-super-do-in-python – jonrsharpe Oct 11 '19 at 08:16

2 Answers2

2

Python's method resolution order works from Left to Right. It will only call the init method of the first class(A in your case). This will give you the desired result-

class A():
def __init__(self):
    super().__init__()
    self.x = 1

# parent class B
class B():
    def __init__(self):
        super().__init__()
        self.y = 2

# parent class C
class C():
    def __init__(self):
        self.z = 3

# target class D
class D(A, B, C):
    def __init__(self):
        super().__init__()

d = D()
print(vars(d))
Saurabh Sangwan
  • 417
  • 2
  • 6
0

In Python, when a class inherits from multiple parent classes, it is called multiple inheritance. In the given code, classes A, B, and C are individual classes without any direct parent classes, but they are used as base classes for the target class D. Even though these classes don't have explicit parent classes, they still require super().init() calls in their init methods because they participate in the method resolution order (MRO) used by Python to determine the order in which the init methods of the parent classes are called.

The Method Resolution Order (MRO) is a specific order in which Python searches for methods and attributes in a class hierarchy. It is determined using the C3 linearization algorithm, which helps to resolve potential ambiguities and conflicts that may arise in multiple inheritance scenarios.

In your example, when you create an instance of class D, it inherits from classes A, B, and C. The MRO is determined in a left-to-right, depth-first manner, so it follows the order of the base classes:

MRO(D) = [D, A, B, C]

Let's break down why the super().init() calls are necessary in each class:

Class A: It has no parent classes, but it's still included in the MRO because it is part of the inheritance chain for class D. The super().init() call in class A will call the init method of class B (the next class in the MRO), which in turn will call the init method of class C. Although class A doesn't have any initialization logic of its own, it is essential to call super().init() to maintain the proper MRO and ensure that the initialization logic of the other classes is executed.

Class B: Similarly to class A, class B doesn't have any direct parent classes but participates in the MRO as part of the inheritance chain for class D. The super().init() call in class B will call the init method of class C, which is the next class in the MRO.

Class C: Class C is the last class in the MRO for class D, and it initializes the attribute z with the value 3.

Class D: Finally, when you create an instance of class D, its init method will be called, and it calls super().init() to trigger the initialization process for classes A, B, and C in the correct order based on the MRO.

As a result, when you create an instance of class D, all the init methods of classes A, B, and C are executed in the correct order, and you end up with an object that has attributes x, y, and z, each initialized with their respective values.

To summarize, even though classes A and B don't have direct parent classes, they are part of the multiple inheritance chain for class D, and the super().init() calls are needed to ensure that the proper MRO is followed, and the initialization logic of all base classes is executed correctly.