Is there a difference between using super()
and using the parent class name directly? For example:
class Parent:
def __init__(self):
print("In parent")
self.__a=10
class Child(Parent):
def __init__(self):
super().__init__() # using super()
Parent.__init__(self) # using Parent class name
c=Child()
Is there internally a difference between super().__init__()
and Parent.__init__(self)
?
Perceive you don't pass the "self" - it is inserted automatically.
super()
was first designed in Python 2 to allow classes to be reused as mixins in a class hierarchy in a way that their immediate superclass may change:Let's supose at some point in time your code is like:
At this point, correct OOP code should execute
C.__init__
which should chain the call toB.__init__
: but when the superclass name is hardcoded that does not happen -A
's__init__
would always come next. And if you hardcodeB.__init__
inC
, you would preventC
from working withoutB
, defeating the purpose of multiple inheritance.When you use
super()
instead, Python's perform the method search for the next parent class looking on the class's__mro__
attribute (mro = method resolution order.__mro__
is a concrete attribute attached to each Python class). - So, if at some point in time classD
above no longer inherits fromB
, the calls tosuper().__init__
inC
will be automatically re-routed straight toA
.It is also worth noting that in Python 3 the parameterless form of
super
was introduced to ease its use - prior to that, one had to hardcode a reference to the own class and also insertself
in the parameters. This form is one of the few exceptions in Python that is hardcoded in the compiler itself - it do change things internally on methods whensuper
(or__class__
) is seen inside a method body (namely, it creates a__class__
variable pointing to the class itself which thesuper
call uses)Not in this case. But in general, and especially when you use multiple inheritance,
super()
delegates to the next object in the Method Resolution Order (MRO) as is specified in the documentation:(copied, boldface added)
Say for instance you define classes like (borrowed from this question, where the MRO is discussed in more detail):
Then the
__mro__
ofA
is:Now if we call
A()
, it prints:so it means that in the context of
A
and when trying to obtain__init__
that:super().__init__
ofA
isD.__init__
;super().__init__
ofD
isB.__init__
;super().__init__
ofB
isC.__init__
;super().__init__
ofC
isE.__init__
;super().__init__
ofE
isG.__init__
;super().__init__
ofG
isH.__init__
;super().__init__
ofH
isF.__init__
; andsuper().__init__
ofF
isobject.__init__
.Note thus that
super()
does not per se delegates to a parent. For instance thesuper()
ofD
isB
andB
is not a superclass ofD
, so it really depends on the type of the object (not on the class).Now in case of
D
, the__mro__
is:If we construct a
D
however we get:So in the context of
D
it holds that:super().__init__
ofD
isE.__init__
;super().__init__
ofE
isG.__init__
;super().__init__
ofG
isH.__init__
;super().__init__
ofH
isF.__init__
; andsuper().__init__
ofF
isobject.__init__
.So here the
super()
ofD
leads toE
(for__init__
) which is not the same in the context ofA
.