3

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}
10
  • 1
    You haven't just removed the common ancestor, you've removed the super calls. Also you've mistyped __init in both examples. Commented Oct 11, 2019 at 7:33
  • I fixed the typo error. I don't understand the rest of your comment. Commented Oct 11, 2019 at 7: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. Commented Oct 11, 2019 at 7:41
  • 1
    They inherit from object. Commented Oct 11, 2019 at 7:46
  • 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() [<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]. 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? Commented Oct 11, 2019 at 7:52

2 Answers 2

3

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))
Sign up to request clarification or add additional context in comments.

3 Comments

I think the init function of class C need a super().__init__() too?
Its the last class in Method-Resolution Order, I believe it's not required there.
Can someone explain why this works?
2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.