Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mixing super and classic calls in Python

Tags:

python

firstly, let me quote a bit an essay from "Expert Python Programming" book:

In the following example, a C class that calls its base classes using the __init__ method will make B class be called twice!

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)

print "MRO:", [x.__name__ for x in C.__mro__]  #prints MRO: ['C', 'A', 'B', 'object']
C()  #prints C A B B

and finally, here is an explanation of what's going on here:

This happens due to the A.__init__(self) call, which is made with the C instance, thus making super(A, self).__init__() call B's constructor. In other words, super should be used into the whole class hierarchy. The problem is that sometimes a part of this hierarchy is located in third-party code.

i have no idea why "super(A, self).__init__() calls B's constructor". Please explain this moment. Thanks a lot.

like image 221
varnie Avatar asked Sep 17 '10 13:09

varnie


2 Answers

To understand this behaviour, you have to understand that super calls not the base class, but searches the next matching method along the order in the __mro__. So, the call super(A, self).__init__() looks at the __mro__ == ['C', 'A', 'B', 'object'], sees B as the next class with a matching method and calls the method (constructor) of B.

If you change C to

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

you get

MRO: ['C', 'A', 'B', 'object']
C1
A
B
C2
B
C3

which shows how the constructor of A calls B.

like image 68
stephan Avatar answered Sep 18 '22 16:09

stephan


The documentation for super says that:

Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped.

When you execute A.__init__(self) from within C the super(A, self) will return <super: <class 'A'>, <C object>>. As the instance is C (<C object>) all the classes in C's inheritance hierarchy are picked up. And the __init__ call is issued on all of them. Consequently you see 'B' getting called twice.

To verify this add another class 'Z' and let 'C' inherit from 'Z' as well. See what happens.

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

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

In this case, A will call B and Z. B will call Z as well.

like image 33
Manoj Govindan Avatar answered Sep 21 '22 16:09

Manoj Govindan