Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a super method work in python in case of multiple inheritance?

How does a super method actually works in python?

In the given code:

class A(object):
    def test(self):
        return 'A'


class B(A):
    def test(self):
        return 'B->'+super(B, self).test()


class C(A):
    def test(self):
        return 'C'


class D(B,C):
    pass


print B().test()    # B->A
print D().test()    # B->C  ????

# MRO of classes are as
print 'mro of A', A.__mro__   # [A,object]
print 'mro of B', B.__mro__   # [B,A,object]
print 'mro of C', C.__mro__   # [C,A,object]
print 'mro of D', D.__mro__   # [D,B,C,A,object]

when test is called on D, it output B->C instead of B->A (which I was expecting).

How is super inside B referring to an instance of C?

like image 488
Kuldeep Rishi Avatar asked Apr 05 '14 10:04

Kuldeep Rishi


2 Answers

From the docs:

super(type[, object-or-type]): Return a proxy object that delegates method calls to a parent or sibling class of type

Emphasis mine.

The MRO (as you have printed out) determines the method resolution order, which in this case is (D, B, C, A, object). So, super(B, self) when called within D returns a proxy object that will delegate methods to its sibling C, based on the MRO.

This can actually be used to extend behavior in inheritance hierarchies without modifying existing classes. Notice that B doesn't actually know which class its test() method is going to be dispatched to -- it simply uses an indirect super() call. You can insert a different class in the MRO by simply creating a new class that mixes multiple base classes in the required order.

For example, assume that your original hierarchy only contained classes A, B and D. Suppose, at a later time, you needed to instrument all of B's method calls with logging. Instead of modifying B you could simply create a new class C that does the logging and have D inherit C, thus inserting it in the MRO right after B.

like image 69
spinlok Avatar answered Nov 03 '22 00:11

spinlok


In common case, super() takes two arguments: a class and an instance of that class. (1) The instance object (self) determines which MRO will be used to resolve any attributes. (2) The provided class determines a subset of that MRO, because super() only uses those entries in the MRO that occur after the class provided.

Thus in above case when super inside B is called from instance of D Object the self refer to D object (ie instance object) and mro of D (ie instance object) is [D,B,C,A] therefore according to (2) only those entries in MRO which occur after B will be used. ie [C,A]

Thus the recommended usage is to provide the class where super() was used as the first argument, and the standard self as the second argument. The resulting object will retain the instance namespace dictionary of self, but it only retrieves attributes that were defined on the classes found later in the MRO than the class provided.

Hence if we redefine B as

class B(A):
    def test(self):
        return 'B->'+super(C, self).test()

then calling D().test() will output ==> 'B->A' explanation: from (1) above MRO of self == [D,B,C,A,object] from (2) above only those entries of MRO will be used which occur after provided class (ie C) hence super will call test method of A.

like image 33
Kuldeep Rishi Avatar answered Nov 03 '22 00:11

Kuldeep Rishi