Marii Marii - 4 months ago 15
Python Question

Does super try each class in MRO

class Base(object):
def m(self):
print 'base'


class MixinA(Base):
def m(self):
super(MixinA, self).m()
print 'mixin a'


class MixinB(Base):
def m(self):
super(MixinB, self).m()
print 'mixin b'


class Top(MixinB, MixinA, Base):
def m(self):
super(Top, self).m()
print 'top'


t = Top()
t.m()


This prints:

base
mixin a
mixin b
top


I'm surprised by multiple things. First MRO of
Top
is
(<class 'Top'>, <class 'MixinB'>, <class 'MixinA'>, <class 'Base'>, <type 'object'>)



  1. Why
    mixin a
    comes before
    mixin b
    ?

  2. Does
    super
    try every class in MRO (unlike searching for an attribute when the first attribute found is returned)?


Answer

No, super() does not 'try' each class in the MRO. Your code chains the calls, because each method called has another super() call in it. Top.m() calls super().m(), which resolves to MixinB.m(); which in turn uses super() again, etc.

mixin a is printed before mixin b because you are printing after the super() call, so the last element in the MRO is executed first. The super() call is just another method call, so a print statement after such a call won't be executed until the super().m() call has completed.

Your MRO is as follows:

>>> type(t).__mro__
(<class '__main__.Top'>, <class '__main__.MixinB'>, <class '__main__.MixinA'>, <class '__main__.Base'>, <type 'object'>)

so naturally Base.m() is called last and gets to print first, followed by MixinA, then MixinB, and Top being the last to print.

Note that the MRO of self is used, not of the class you pass into super() as the first argument; the MRO is thus stable throughout all calls in your hierarchy for any given instance.

If you expected the print statements to be executed in the order the MRO calls are chained, you'll have to put the print statements before calling the next m() method in the MRO.