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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Jinghui Niu
  • 990
  • 11
  • 28
  • Could the down-voter please explain a bit why you down voted my question?? I honestly don't understand. Thx – Jinghui Niu Sep 10 '16 at 07:45
  • Each method calls `super().__init__()`, **not** `A.__init__()`. Why do you think that `A.__init__` is the same thing in these cases? – Martijn Pieters Sep 14 '16 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](https://www.python.org/download/releases/2.3/mro/) to see why and how. – Martijn Pieters Sep 14 '16 at 10:24

1 Answers1

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

Farzan Hajian
  • 1,799
  • 17
  • 27
  • 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. – Jinghui Niu Sep 11 '16 at 03:24
  • 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 Sep 11 '16 at 05:27
  • @ 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? – Jinghui Niu Sep 11 '16 at 09:32
  • @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 Hajian Sep 11 '16 at 11:39
  • @ 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. – Jinghui Niu Sep 13 '16 at 06:15
  • @JinghuiNiu that's simply because you first call super() and then call print() in __init__() methods. So in each __init__() method, super class __init__() methods are called first (and they print their messages) and then the print() method is called – Farzan Hajian Sep 15 '16 at 08:48