I\'m pretty much new in Python object oriented programming and I have trouble
understanding the super()
function (new style classes) especially when it comes to
In python 3.5+ inheritance looks predictable and very nice for me. Please looks at this code:
class Base(object):
def foo(self):
print(" Base(): entering")
print(" Base(): exiting")
class First(Base):
def foo(self):
print(" First(): entering Will call Second now")
super().foo()
print(" First(): exiting")
class Second(Base):
def foo(self):
print(" Second(): entering")
super().foo()
print(" Second(): exiting")
class Third(First, Second):
def foo(self):
print(" Third(): entering")
super().foo()
print(" Third(): exiting")
class Fourth(Third):
def foo(self):
print("Fourth(): entering")
super().foo()
print("Fourth(): exiting")
Fourth().foo()
print(Fourth.__mro__)
Outputs:
Fourth(): entering
Third(): entering
First(): entering Will call Second now
Second(): entering
Base(): entering
Base(): exiting
Second(): exiting
First(): exiting
Third(): exiting
Fourth(): exiting
(<class '__main__.Fourth'>, <class '__main__.Third'>, <class '__main__.First'>, <class '__main__.Second'>, <class '__main__.Base'>, <class 'object'>)
As you can see, it calls foo exactly ONE time for each inherited chain in the same order as it was inherited. You can get that order by calling .mro :
Fourth -> Third -> First -> Second -> Base -> object
This is known as the Diamond Problem, the page has an entry on Python, but in short, Python will call the superclass's methods from left to right.
Consider calling super().Foo()
called from a sub-class. The Method Resolution Order (MRO) method is the order in which method calls are resolved.
In this, super().Foo() will be searched up in the hierarchy and will consider the closest implementation, if found, else raise an Exception. The "is a" relationship will always be True in between any visited sub-class and its super class up in the hierarchy. But this story isn't the same always in Multiple Inheritance.
Here, while searching for super().Foo() implementation, every visited class in the hierarchy may or may not have is a relation. Consider following examples:
class A(object): pass
class B(object): pass
class C(A): pass
class D(A): pass
class E(C, D): pass
class F(B): pass
class G(B): pass
class H(F, G): pass
class I(E, H): pass
Here, I
is the lowest class in the hierarchy. Hierarchy diagram and MRO for I
will be
(Red numbers showing the MRO)
MRO is I E C D A H F G B object
Note that a class X
will be visited only if all its sub-classes, which inherit from it, have been visited(i.e., you should never visit a class that has an arrow coming into it from a class below that you have not yet visited).
Here, note that after visiting class C
, D
is visited although C
and D
DO NOT have is a relationship between them(but both have with A
). This is where super()
differs from single inheritance.
Consider a slightly more complicated example:
(Red numbers showing the MRO)
MRO is I E C H D A F G B object
In this case we proceed from I
to E
to C
. The next step up would be A
, but we have yet to visit D
, a subclass of A
. We cannot visit D
, however, because we have yet to visit H
, a subclass of D
. The leaves H
as the next class to visit. Remember, we attempt to go up in hierarchy, if possible, so we visit its leftmost superclass, D
. After D
we visit A
, but we cannot go up to object because we have yet to visit F
, G
, and B
. These classes, in order, round out the MRO for I
.
Note that no class can appear more than once in MRO.
This is how super() looks up in the hierarchy of inheritance.
Credits for resources: Richard L Halterman Fundamentals of Python Programming
This is to how I solved to issue of having multiple inheritance with different variables for initialization and having multiple MixIns with the same function call. I had to explicitly add variables to passed **kwargs and add a MixIn interface to be an endpoint for super calls.
Here A
is an extendable base class and B
and C
are MixIn classes both who provide function f
. A
and B
both expect parameter v
in their __init__
and C
expects w
.
The function f
takes one parameter y
. Q
inherits from all three classes. MixInF
is the mixin interface for B
and C
.
class A(object):
def __init__(self, v, *args, **kwargs):
print "A:init:v[{0}]".format(v)
kwargs['v']=v
super(A, self).__init__(*args, **kwargs)
self.v = v
class MixInF(object):
def __init__(self, *args, **kwargs):
print "IObject:init"
def f(self, y):
print "IObject:y[{0}]".format(y)
class B(MixInF):
def __init__(self, v, *args, **kwargs):
print "B:init:v[{0}]".format(v)
kwargs['v']=v
super(B, self).__init__(*args, **kwargs)
self.v = v
def f(self, y):
print "B:f:v[{0}]:y[{1}]".format(self.v, y)
super(B, self).f(y)
class C(MixInF):
def __init__(self, w, *args, **kwargs):
print "C:init:w[{0}]".format(w)
kwargs['w']=w
super(C, self).__init__(*args, **kwargs)
self.w = w
def f(self, y):
print "C:f:w[{0}]:y[{1}]".format(self.w, y)
super(C, self).f(y)
class Q(C,B,A):
def __init__(self, v, w):
super(Q, self).__init__(v=v, w=w)
def f(self, y):
print "Q:f:y[{0}]".format(y)
super(Q, self).f(y)