5
 A      B
| |   |   |
C D   E   F
| |   |   |
    G     H
      |
      I


user@ubuntu:~/Documents/Python/oop_python$ cat tt.py
class A:
    def call_me(self):
        print("A")

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

class D(A):
    def call_me(self):
        super().call_me()
        print("D")

class B:
    def call_me(self):
        print("B")

class E(B):
    def call_me(self):
        super().call_me()
        print("E")

class F(B):
    def call_me(self):
        super().call_me()
        print("F")

class G(C, D, E):
    def call_me(self):
        super().call_me()
        print("G")

class H(F):
    def call_me(self):
        super().call_me()
        print("H")

class I(G, H):
    def call_me(self):
        super().call_me()
        print("I")


user@ubuntu:~/Documents/Python/oop_python$ python3.2 -i tt.py
>>> i = I()
>>> i.call_me()
A
D
C
G
I

Question> Why B, E, F are not printed?

//            updated based on comments from delnan

user@ubuntu:~/Documents/Python/oop_python$ cat tt.py
class BaseClass():
    def call_me(self):
        print("BaseClass")
    pass

class A(BaseClass):
    def call_me(self):
        super().call_me()
        print("A")

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

class D(A):
    def call_me(self):
        super().call_me()
        print("D")

class B(BaseClass):
    def call_me(self):
        super().call_me()
        print("B")

class E(B):
    def call_me(self):
        super().call_me()
        print("E")

class F(B):
    def call_me(self):
        super().call_me()
        print("F")

class G(C, D, E):
    def call_me(self):
        super().call_me()
        print("G")

class H(F):
    def call_me(self):
        super().call_me()
        print("H")

class I(G, H):
    def call_me(self):
        super().call_me()
        print("I")

user@ubuntu:~/Documents/Python/oop_python$ python3.2 -i tt.py
>>> i = I()
>>> i.call_me()
BaseClass
B
F
H
E
A
D
C
G
I
2

2 Answers 2

7

A common misunderstanding is that super() will call all superclasses methods. It will not. It will call only one of them. Which one is automatically calculated by super() according to some specific rules. Sometimes the one it calls is not an actual super-class, but a sibling. But there is no guarantee that all will be called, unless all of the classes in their turn are using super().

In this case, A and B is not calling super. And if you add it to A, it will in fact call the "missing" classes, but if you add it to B you get an error, because in this particular case, B will end up being the "last" (or first, depending on how you see it) class.

If you want to use super(), the best solution is to have a common baseclass for A and B that implements call_me, but does not call super(). (Thanks to delnan for suggesting that).

However, if you know your class hierarchy, you can call the superclasses methods directly instead of using super(). Note that this in the case above, doesn't mean that every class must call each of it's baseclasses directly. This is therefore not useful in cases where you as a programmer don't have complete control over the class hierarchy, for example if you write libraries or mixin-classes. Then you have to use super().

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

4 Comments

If one uses super consistently and correctly (here: calling super in A and B and adding a common base class that does not call super), all superclass methods are called exactly once. Your suggestions doesn't preserve that property. If one explicitly calls all super class's methods, classes that are inherited multiple times have their method calls multiple times. For instance, if both C and D call A.call_me and G calls C.call_me and D.call_me, G().call_me() leads to A.call_me being called twice.
Yeah, having a common baseclass is a good suggestion in this case. Yes, calling the classes directly means you also have to keep your tongue straight. It can be hard to do in cases as complex as this, but then again, if you have cases as complex as this, you are probably doing it wrong it the first place.
Well, the complexity of the class hierachie is part of the problem. But multiple inheritance is inherently complex. "Just call all super class methods" is too simplistic, it fails (i.e. calls methods more than once) in nearly all cases of multiple inheritance (e.g. every time an ancestor is reachable through more than one inheritance path), whereas super works perfectly with a bit of discipline on everyone's part. I wouldn't be so quick to dismiss it.
If you think I dismiss it, you are misreading me. But anyway, multiple inheritance is very seldom so complex that you end up having a class (except baseclasses who do little but provide interfaces, as in your suggestion) inherited multiple times. As a Zope developer I used multiple inheritance for many years before super() with nothing as complicated as this, and no problems. I agree super() works with a bit of discipline, but so does calling things directly, as long as you have control over the class hierarchy. (As mentioned, libraries or mixin-classes do not apply).
1

The precondition to use super is the class must be the new-style.

There are two use cases for super in python:

  1. A class hierarchy with single inheritance, super can use to refer to parent class without naming them explicitly

  2. Second use case is support cooperative multiple inheritance in a dynamic execution environment.

So, the second case is matching your second example condition. In this case the subclass will do a bfs traverse to make sure each overwrite base class method is called only once. Your inheritance tree could be rewrite to liner as this order (follow the bfs traverse, left to right) I G C D A E H F B BaseClass.

The definition of the super method is:

Super(type[,object-or-type])

It will return a proxy object that delegates method calls to a parent or sibling class of type. In python 3.x you could call super() directly which is as same as super(currentClassName, self). This will get the proxy of the direct right super|sibling class generated by the bfs traverse. So the method i.call_me() will be called as:

I.call_me -> G.call_me -> C.call_me -> D.call_me -> A.call_me -> E.call_me -> H.call_me -> F.call_me -> B.call_me -> BaseClass.call_me

The difference between the two examples

As you asked why the first example didn't print B, E, F, because the inheritance map is not a "diamond graph" which means the A.call_me B.call_me are different method. So python only choose one of the inheritance branch.

Wish this helpful. You could also check the python class doc for more detail

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.