What does 'super' do in Python? - difference between super().__init__() and explicit superclass __init__()
Solution 1
The benefits of super()
in single-inheritance are minimal -- mostly, you don't have to hard-code the name of the base class into every method that uses its parent methods.
However, it's almost impossible to use multiple-inheritance without super()
. This includes common idioms like mixins, interfaces, abstract classes, etc. This extends to code that later extends yours. If somebody later wanted to write a class that extended Child
and a mixin, their code would not work properly.
Solution 2
What's the difference?
SomeBaseClass.__init__(self)
means to call SomeBaseClass
's __init__
. while
super().__init__()
means to call a bound __init__
from the parent class that follows SomeBaseClass
's child class (the one that defines this method) in the instance's Method Resolution Order (MRO).
If the instance is a subclass of this child class, there may be a different parent that comes next in the MRO.
Explained simply
When you write a class, you want other classes to be able to use it. super()
makes it easier for other classes to use the class you're writing.
As Bob Martin says, a good architecture allows you to postpone decision making as long as possible.
super()
can enable that sort of architecture.
When another class subclasses the class you wrote, it could also be inheriting from other classes. And those classes could have an __init__
that comes after this __init__
based on the ordering of the classes for method resolution.
Without super
you would likely hard-code the parent of the class you're writing (like the example does). This would mean that you would not call the next __init__
in the MRO, and you would thus not get to reuse the code in it.
If you're writing your own code for personal use, you may not care about this distinction. But if you want others to use your code, using super
is one thing that allows greater flexibility for users of the code.
Python 2 versus 3
This works in Python 2 and 3:
super(Child, self).__init__()
This only works in Python 3:
super().__init__()
It works with no arguments by moving up in the stack frame and getting the first argument to the method (usually self
for an instance method or cls
for a class method - but could be other names) and finding the class (e.g. Child
) in the free variables (it is looked up with the name __class__
as a free closure variable in the method).
I used to prefer to demonstrate the cross-compatible way of using super
, but now that Python 2 is largely deprecated, I will demonstrate the Python 3 way of doing things, that is, calling super
with no arguments.
Indirection with Forward Compatibility
What does it give you? For single inheritance, the examples from the question are practically identical from a static analysis point of view. However, using super
gives you a layer of indirection with forward compatibility.
Forward compatibility is very important to seasoned developers. You want your code to keep working with minimal changes as you change it. When you look at your revision history, you want to see precisely what changed when.
You may start off with single inheritance, but if you decide to add another base class, you only have to change the line with the bases - if the bases change in a class you inherit from (say a mixin is added) you'd change nothing in this class.
In Python 2, getting the arguments to super
and the correct method arguments right can be a little confusing, so I suggest using the Python 3 only method of calling it.
If you know you're using super
correctly with single inheritance, that makes debugging less difficult going forward.
Dependency Injection
Other people can use your code and inject parents into the method resolution:
class SomeBaseClass(object):
def __init__(self):
print('SomeBaseClass.__init__(self) called')
class UnsuperChild(SomeBaseClass):
def __init__(self):
print('UnsuperChild.__init__(self) called')
SomeBaseClass.__init__(self)
class SuperChild(SomeBaseClass):
def __init__(self):
print('SuperChild.__init__(self) called')
super().__init__()
Say you add another class to your object, and want to inject a class between Foo and Bar (for testing or some other reason):
class InjectMe(SomeBaseClass):
def __init__(self):
print('InjectMe.__init__(self) called')
super().__init__()
class UnsuperInjector(UnsuperChild, InjectMe): pass
class SuperInjector(SuperChild, InjectMe): pass
Using the un-super child fails to inject the dependency because the child you're using has hard-coded the method to be called after its own:
>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called
However, the class with the child that uses super
can correctly inject the dependency:
>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called
Addressing a comment
Why in the world would this be useful?
Python linearizes a complicated inheritance tree via the C3 linearization algorithm to create a Method Resolution Order (MRO).
We want methods to be looked up in that order.
For a method defined in a parent to find the next one in that order without super
, it would have to
- get the mro from the instance's type
- look for the type that defines the method
- find the next type with the method
- bind that method and call it with the expected arguments
The
UnsuperChild
should not have access toInjectMe
. Why isn't the conclusion "Always avoid usingsuper
"? What am I missing here?
The UnsuperChild
does not have access to InjectMe
. It is the UnsuperInjector
that has access to InjectMe
- and yet cannot call that class's method from the method it inherits from UnsuperChild
.
Both Child classes intend to call a method by the same name that comes next in the MRO, which might be another class it was not aware of when it was created.
The one without super
hard-codes its parent's method - thus is has restricted the behavior of its method, and subclasses cannot inject functionality in the call chain.
The one with super
has greater flexibility. The call chain for the methods can be intercepted and functionality injected.
You may not need that functionality, but subclassers of your code may.
Conclusion
Always use super
to reference the parent class instead of hard-coding it.
What you intend is to reference the parent class that is next-in-line, not specifically the one you see the child inheriting from.
Not using super
can put unnecessary constraints on users of your code.
Solution 3
I had played a bit with super()
, and had recognized that we can change calling order.
For example, we have next hierarchy structure:
A
/ \
B C
\ /
D
In this case MRO of D will be (only for Python 3):
In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)
Let's create a class where super()
calls after method execution.
In [23]: class A(object): # or with Python 3 can define class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: print("I'm from B")
...: super().__init__()
...:
...: class C(A):
...: def __init__(self):
...: print("I'm from C")
...: super().__init__()
...:
...: class D(B, C):
...: def __init__(self):
...: print("I'm from D")
...: super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A
A
/ ⇖
B ⇒ C
⇖ /
D
So we can see that resolution order is same as in MRO. But when we call super()
in the beginning of the method:
In [21]: class A(object): # or class A:
...: def __init__(self):
...: print("I'm from A")
...:
...: class B(A):
...: def __init__(self):
...: super().__init__() # or super(B, self).__init_()
...: print("I'm from B")
...:
...: class C(A):
...: def __init__(self):
...: super().__init__()
...: print("I'm from C")
...:
...: class D(B, C):
...: def __init__(self):
...: super().__init__()
...: print("I'm from D")
...: d = D()
...:
I'm from A
I'm from C
I'm from B
I'm from D
We have a different order it is reversed a order of the MRO tuple.
A
/ ⇘
B ⇐ C
⇘ /
D
For additional reading I would recommend next answers:
- C3 linearization example with super (a large hierarchy)
- Important behavior changes between old and new style classes
- The Inside Story on New-Style Classes
Solution 4
Doesn't all of this assume that the base class is a new-style class?
class A:
def __init__(self):
print("A.__init__()")
class B(A):
def __init__(self):
print("B.__init__()")
super(B, self).__init__()
Will not work in Python 2. class A
must be new-style, i.e: class A(object)
Solution 5
When calling super()
to resolve to a parent's version of a classmethod, instance method, or staticmethod, we want to pass the current class whose scope we are in as the first argument, to indicate which parent's scope we're trying to resolve to, and as a second argument the object of interest to indicate which object we're trying to apply that scope to.
Consider a class hierarchy A
, B
, and C
where each class is the parent of the one following it, and a
, b
, and c
respective instances of each.
super(B, b)
# resolves to the scope of B's parent i.e. A
# and applies that scope to b, as if b was an instance of A
super(C, c)
# resolves to the scope of C's parent i.e. B
# and applies that scope to c
super(B, c)
# resolves to the scope of B's parent i.e. A
# and applies that scope to c
Using super
with a staticmethod
e.g. using super()
from within the __new__()
method
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Explanation:
1- even though it's usual for __new__()
to take as its first param a reference to the calling class, it is not implemented in Python as a classmethod, but rather a staticmethod. That is, a reference to a class has to be passed explicitly as the first argument when calling __new__()
directly:
# if you defined this
class A(object):
def __new__(cls):
pass
# calling this would raise a TypeError due to the missing argument
A.__new__()
# whereas this would be fine
A.__new__(A)
2- when calling super()
to get to the parent class we pass the child class A
as its first argument, then we pass a reference to the object of interest, in this case it's the class reference that was passed when A.__new__(cls)
was called. In most cases it also happens to be a reference to the child class. In some situations it might not be, for instance in the case of multiple generation inheritances.
super(A, cls)
3- since as a general rule __new__()
is a staticmethod, super(A, cls).__new__
will also return a staticmethod and needs to be supplied all arguments explicitly, including the reference to the object of insterest, in this case cls
.
super(A, cls).__new__(cls, *a, **kw)
4- doing the same thing without super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Using super
with an instance method
e.g. using super()
from within __init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Explanation:
1- __init__
is an instance method, meaning that it takes as its first argument a reference to an instance. When called directly from the instance, the reference is passed implicitly, that is you don't need to specify it:
# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...
# you create an instance
a = A()
# you call `__init__()` from that instance and it works
a.__init__()
# you can also call `__init__()` with the class and explicitly pass the instance
A.__init__(a)
2- when calling super()
within __init__()
we pass the child class as the first argument and the object of interest as a second argument, which in general is a reference to an instance of the child class.
super(A, self)
3- The call super(A, self)
returns a proxy that will resolve the scope and apply it to self
as if it's now an instance of the parent class. Let's call that proxy s
. Since __init__()
is an instance method the call s.__init__(...)
will implicitly pass a reference of self
as the first argument to the parent's __init__()
.
4- to do the same without super
we need to pass a reference to an instance explicitly to the parent's version of __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Using super
with a classmethod
class A(object):
@classmethod
def alternate_constructor(cls, *a, **kw):
print "A.alternate_constructor called"
return cls(*a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
Explanation:
1- A classmethod can be called from the class directly and takes as its first parameter a reference to the class.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- when calling super()
within a classmethod to resolve to its parent's version of it, we want to pass the current child class as the first argument to indicate which parent's scope we're trying to resolve to, and the object of interest as the second argument to indicate which object we want to apply that scope to, which in general is a reference to the child class itself or one of its subclasses.
super(B, cls_or_subcls)
3- The call super(B, cls)
resolves to the scope of A
and applies it to cls
. Since alternate_constructor()
is a classmethod the call super(B, cls).alternate_constructor(...)
will implicitly pass a reference of cls
as the first argument to A
's version of alternate_constructor()
super(B, cls).alternate_constructor()
4- to do the same without using super()
you would need to get a reference to the unbound version of A.alternate_constructor()
(i.e. the explicit version of the function). Simply doing this would not work:
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return A.alternate_constructor(cls, *a, **kw)
The above would not work because the A.alternate_constructor()
method takes an implicit reference to A
as its first argument. The cls
being passed here would be its second argument.
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
# first we get a reference to the unbound
# `A.alternate_constructor` function
unbound_func = A.alternate_constructor.im_func
# now we call it and pass our own `cls` as its first argument
return unbound_func(cls, *a, **kw)
Admin
Updated on July 08, 2022Comments
-
Admin almost 2 years
What's the difference between:
class Child(SomeBaseClass): def __init__(self): super(Child, self).__init__()
and:
class Child(SomeBaseClass): def __init__(self): SomeBaseClass.__init__(self)
I've seen
super
being used quite a lot in classes with only single inheritance. I can see why you'd use it in multiple inheritance but am unclear as to what the advantages are of using it in this kind of situation. -
overexchange almost 7 yearsIn C, DI is like this. code is here. If I add one more implementation of
list
interface, saydoublylinkedlist
then the application smoothly picks it. I can make my example more configurable by introducingconfig.txt
and link implementation at load time. Is this the right example? If yes,How do I relate your code? See the first adv of DI in wiki. Where is any new implementation configurable? in your code -
Russia Must Remove Putin almost 7 yearsA new implementation is created through inheritance, for example, where one of the "Injector" classes inherits from the
InjectMe
class. Comments aren't for discussion, however, so I suggest you discuss this further with others in chat or ask a new question on the main site. -
JJson over 5 yearsI dont understand why the order is changing. The first part I understand that D-B-C-A because D is the first class, then when load the self(B,C) will eventually print B, C then only A since B(A),C(A) pointed back to self for the final part. If I follow this understanding, then shouldnt the second part be like B-C-A-D? Could you please explain a little to me please.
-
JJson over 5 yearsMy bad, I didnt notice that every each class instance has been initiated with super() first. Then if that is the case, shouldnt it be A-B-C-D? I somehow understand how A-C-B-D came to but still couldnt convince and still have a bit confuse. my understanding is that, d = D() called the Class D(B,C) with 2 self-parameters, since super() is initiated first then B is called together with it's attributes then D is not printed before C is because Class D(B,C) contains 2 self-parameters so it must execute the second one which is Class C(A), after executed there is not more self-parameters to executed
-
JJson over 5 yearsthen it will print C then print B and finally print D. Am I right?
-
Charlie Parker about 5 yearscan you provide an example by what you mean that with "it would not work properly"?
-
Aviad Rozenhek almost 5 yearsgreat answer! but when using multiple inheritance, there are complications with super() and
__init__
functions. especially if the signature of__init__
varies between classes in the hierarchy. I've added an answer that focuses on this aspect -
grant sun over 4 yearsIt's very easy to understand the second one as long as you get the first one. It is just like a stack. you push the print'' into stack and do super(), when it's done the A, it starts to print things in that stack, so the order is reverse.
-
Eli Halych over 3 yearsIt is like a recursion. What it does in the second example, it calls all classes first, puts them in the queue (or stack) because super() is called first. Then when it gets to the base class, it executes the base class' print method and goes down to the next one in the queue (or in the stack as @grantsun said). And in the first example D's print() is called first that's why it prints "I'm from D" first and only then it goes to the next class where it again sees print() first and again only then calls super()
-
Roman Shapovalov almost 3 yearsThank you for this super()-verbose answer! I could not find anywhere else how the second argument is deduced in the Python 3 syntax (i.e. “moving up in the stack frame and getting the first argument to the method”). Seems strange they settled with this implicit syntax: less typing but somewhat inconsistent with the way things are done elsewhere within the class code where you need to always specify
self
explicitly (e.g. no implicit object-variable resolution). -
Russia Must Remove Putin almost 3 years@RomanShapovalov I recall doing the research on that, but I don't exactly recall where I found it - I think in the super object definition (written in C) in the source code. I would start there if you're looking to verify that the mechanism remains the same. However the mechanism works, it would still be an implementation detail that you're not expected to look at too closely, regardless...
-
zardosht about 2 yearsIf you wonder how the MRO is working (i.e. why the execution jumps from B to C instead of from B to A) check the accepted answer to this question: stackoverflow.com/questions/64740117/…. Basically, the
super()
delegates method calls to a parent or sibling class of type. That is thesuper()
call in B, delegates the call to C (the sibling of B) instead of A (the parent of B).