0

Recently i'm reading a book, it mentioned the mro in multiple inheritance, and give sample code:

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

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

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

C()

output:

C
A
B
B

I wanna know why this happen?

TreeCatCat
  • 143
  • 1
  • 1
  • 12

2 Answers2

2

This happens because you're mixing direct constructor calls and calls via super. Your class hierarchy should do either one or the other.

More precisely, the MRO for the class C is C, A, B. Thus, when you call super(A, self).__init__() in A's constructor, that calls the constructor for B, since B succeeds A in C's MRO. In other words, it is inside A's constructor that the first B is printed.

As an aside, writing out super(A, self) is superfluous in Python 3. super() is enough; the arguments are deduced from the context.

Further reading:

Dolda2000
  • 25,216
  • 4
  • 51
  • 92
0

I've converted your code to Python3 (by changing print to print()) and added some more tracing statements:

class A(object):
    def __init__(self):
        print("A", type(self).__mro__)
        super(A, self).__init__()
        print("/A")

class B(object):
    def __init__(self):
        print("B", type(self).__mro__)
        super(B, self).__init__()
        print("/B")

class C(A,B):
    def __init__(self):
        print("C")
        A.__init__(self)
        print("ca/b")
        B.__init__(self)
        print("/C")

C()

Here's the output:

C
A (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
B (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
/B
/A
ca/b
B (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
/B
/C

Looking at the output, you can see the call to the C.__init__ method is happening, and printing 'C'. It then calls A.__init__ directly (which is a mistake- use super!).

The A.__init__ call prints its message, including the __mro__ attribute. You can see the sequence: C -> A -> B -> Object. This is important, because it means that calls to super() from within A.__init__ are going to refer to class B. Also, calls to super from inside C.__init__ would have invoked A.__init__ automatically, if you let them.

The next method, B.__init__, is invoked by the super reference inside A.__init__, as mentioned. It calls Object.__init__, presumably, which prints nothing. ;-)

Both B.__init__ and A.__init__ return, we see the midpoint message ca/b, and then your direct call to B.__init__ is made. It prints a message, but again a reference to super form B.__init__ does nothing, because B is at the tail end of the MRO.

The super() mechanism can deal with all this MRO stuff for you. You shouldn't have to invoke A.__init__(self). Instead, you can just let super handle it, inside C.__init__, but doing this:

class C(A,B):
    def __init__(self):
        print("C")
        super(C, self).__init__()
aghast
  • 14,785
  • 3
  • 24
  • 56