0

I have the following code:

class A:
    def __init__(self):
        print("A.__init__")

class B(A):
    def __init__(self):
        print("B.__init__")
        super().__init__()

class C(A):
    def __init__(self):
        print("C.__init__")
        super().__init__()


class D(B,C):
    def __init__(self):
        print("D.__init__")
        super().__init__()

When I instantiate an object out of class D,

d = D()

I got:

>>> d = D()
D.__init__
B.__init__
C.__init__
A.__init__

Why did class A's __init__() only got called once, and at the very end? Note both class B and class C called class A's __init__(), so I expected that class A's __init__() should be called twice.

I read both Python 3's documentation and Mr. Hettinger's blog, but didn't figure out the underlying mechanism. So how does the super() determine that it only needs to execute once? Thanks.

3
  • Could the down-voter please explain a bit why you down voted my question?? I honestly don't understand. Thx Commented Sep 10, 2016 at 7:45
  • Each method calls super().__init__(), not A.__init__(). Why do you think that A.__init__ is the same thing in these cases? Commented Sep 14, 2016 at 10:20
  • super() looks at the class MRO (method resolution order). For multiple inheritance (diamond inheritance graph) the MRO is still going to be linear, exactly to avoid calling A.__init__() more than once. Look at D.__mro__ to see the actual MRO determined at runtime, you'll see it is [D, B, C, A]. Read the Python MRO documentation to see why and how. Commented Sep 14, 2016 at 10:24

1 Answer 1

0

Python has a mechanism called Method Resolution Order (MRO) to find proper methods in class hierarchies. There are two rules in MRO:

1- dept-first left-to-right search

2- only the last occurrence of a class is considered

Now back to your example:

I. The search starts at D by adding it to the MRO list [D].

II. Class D is inherited from B and C. B is the left most parent and then comes C. So the MRO list becomes [D, B, C].

III. Then the search continues to a deeper level with is parent of B and A is added to the list. Now we have [D, B, C, A]. The search continues to parents of C with is again A. Since A is already in the list, nothing changes.

IV. The search goes to a deeper level (parents of A) and because A does not have any explicit parent, the search ends at this point.

So the final list is [D, B, C, A].

super() method uses this list to do its job and thats why A is called at the end

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for your answer. I'm having a little trouble understanding your number 2 point(only the last occurrence of a class is considered). What do you refer to when you say "occurrence"? Does invoking its method count as an occurrence? In my example, can I say the class A occurred twice? Thank you very much.
Whenever the search process visits a class, it needs to to add it to the list but if the class has already been added, it is removed and then added to the back of the list. So it is guaranteed that each class is added to the MRO list only once and the last time that the class appears in the search process matters. Hope this helps @JinghuiNiu
@ Farzan Hajian I have a follow-up here, what if I have a situation where I do need to invoke class A's __init__() twice in both B and C? Will the MRO mechanically ignore my plea and only invoke once regardless of the situation? Is there a way to overcome it then?
@JinghuiNiu class A's __init__() is invoked once no matter how many times it appears in the search and unfortunately I don't know any workaround.
@ Farzan I'm still studying your comment, if we switch around the print() line and the super() line for each class' __init__(), I actually got a reversed sequece, i.e. [A, C, B, D], instead of [D,B,C,A]. What's going on here?? Just can't figure this out.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.