1

I was playing with classes to see how mro works in python3. I got the main idea: methods are resolved left to right, depth first and if there is a common parent, it's at the end.

Here is something apparently quite simple that does not behave like I expect:

class A(object):
    def f(self):
        print("calling A")
        print("A")


class B(A):
    def f(self):
        print("calling B")
        super(B, self).f()
        print("B")


class C(A):
    def f(self):
        print("calling C")
        super(C, self).f()
        print("C")


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

d=D()
d.f()

Now, since the mro is D -> B -> C -> A I expect super to call B.f so that it prints:

calling D
calling B
calling A
A
B
D

But it actually call C.f too:

calling D
calling B
calling C
calling A
A        
C        
B        
D        

Why is that?

The reason I expect C.f not to be called is that since B as method f, the resolution should stop. This is the case if C does not inherit from A:

class A(object):
    def f(self):
        print("calling A")
        print("A")


class B(A):
    def f(self):
        print("calling B")
        super(B, self).f()
        print("B")


class C():
    def f(self):
        print("calling C")
        super(C, self).f()
        print("C")


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

d=D()
d.f()
# prints:
# calling D
# calling B
# calling A
# A        
# B        
# D        
little-dude
  • 1,544
  • 2
  • 17
  • 33
  • you said it yourself, the MRO is `D -> B -> C -> A`, so why shouldn't it call `C.f`? `C` is in the MRO after all! – MSeifert Apr 05 '17 at 17:13
  • Well python finds that `B` has the method `f` so it but there is not need to continue. Besides, in my example, if `C` does not inherit from `A`, `C.f` is not called. – little-dude Apr 05 '17 at 17:15
  • 2
    The MRO states in what order the methods are called (at least if each one calls `super`). So I don't understand what you mean about "no need to continue". You do call `super` and as long as you do that it follows the MRO. – MSeifert Apr 05 '17 at 17:18
  • If you want it to stop at `B` then don't call `super` in `B.f`. – MSeifert Apr 05 '17 at 17:19
  • @MSeifert I updated my question with more details why I expect not `C.f` to be called. I hope this clarifies my question. – little-dude Apr 05 '17 at 17:19
  • Ahhhhhhhh. I just realized what you mean @MSeifert. In `B.f`, `super` calls `C.f`. I thought `super` would call `A.f`, which is why this was so confusing. So this is not me misunderstanding MRO, this is me misundertanding `super`. Thanks! – little-dude Apr 05 '17 at 17:22
  • Should I delete this question, or is it worth adding an answer? – little-dude Apr 05 '17 at 17:23
  • @little-dude Just add the answer yourself, so others can see what you came up with. You are allowed to answer your own question. Maybe others will misunderstand super in the future and find the answer helpful – Peter Steele Apr 05 '17 at 17:30

1 Answers1

1

I think it's important to mention that super doesn't call the "parent-class" but the "next class in the MRO".

You said it yourself, that the MRO is D -> B -> C -> A for D. So super in D.f will resolve to B, super in B.f will resolve to C and finally super in C.f will resolve to A.

If you need further information there are several ressources (with different interpretations about the usefulness of super) avaiable:

Community
  • 1
  • 1
MSeifert
  • 145,886
  • 38
  • 333
  • 352