Reputation: 81
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}
Upvotes: 3
Views: 1460
Reputation: 77
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.
Upvotes: 2
Reputation: 427
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))
Upvotes: 3