Why `super()` in Ursina module in Python [duplicate] - python

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.

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 to InjectMe. Why isn't the conclusion "Always avoid using super"? 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.

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.

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

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)

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)

Super() in a nutshell
Every Python instance has a class that created it.
Every class in Python has a chain of ancestor classes.
A method using super() delegates work to the next ancestor in the chain for the instance's class.
Example
This small example covers all the interesting cases:
class A:
def m(self):
print('A')
class B(A):
def m(self):
print('B start')
super().m()
print('B end')
class C(A):
def m(self):
print('C start')
super().m()
print('C end')
class D(B, C):
def m(self):
print('D start')
super().m()
print('D end')
The exact order of calls is determined by the instance the method is called from:
>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()
For instance a, there is no super call:
>>> a.m()
A
For instance b, the ancestor chain is B -> A -> object:
>>> type(b).__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
>>> b.m()
B start
A
B end
For instance c, the ancestor chain is C -> A -> object:
>>> type(c).__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> c.m()
C start
A
C end
For instance d, the ancestor chain is more interesting D -> B -> C -> A -> object (mro stands for method resolution order) :
>>> type(d).__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> d.m()
D start
B start
C start
A
C end
B end
D end
More information
Having answered the question of "What does super do in Python?", the next question is how to use it effectively. See this step-by-step tutorial or this 45 minute video.

Many great answers, but for visual learners:
Firstly lets explore with arguments to super, and then without.
Imagine theres an instance jack created from the class Jack, who has the inheritance chain as shown in green in the picture. Calling:
super(Jack, jack).method(...)
will use the MRO (Method Resolution Order) of jack (its inheritance tree in a certain order), and will start searching from Jack. Why can one provide a parent class? Well if we start searching from the instance jack, it would find the instance method, the whole point is to find its parents method.
If one does not supply arguments to super, its like the first argument passed in is the class of self, and the second argument passed in is self. These are auto-calculated for you in Python3.
However say we dont want to use Jack's method, instead of passing in Jack, we could of passed in Jen to start searching upwards for the method from Jen.
It searches one layer at a time (width not depth), e.g. if Adam and Sue both have the required method, the one from Sue will be found first.
If Cain and Sue both had the required method, Cain's method would be called first.
This corresponds in code to:
Class Jen(Cain, Sue):
MRO is from left to right.

In the case of multiple inheritance, you normally want to call the initializers of both parents, not just the first. Instead of always using the base class, super() finds the class that is next in Method Resolution Order (MRO), and returns the current object as an instance of that class. For example:
class Base(object):
def __init__(self):
print("initializing Base")
class ChildA(Base):
def __init__(self):
print("initializing ChildA")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("initializing ChildB")
super().__init__()
class Grandchild(ChildA, ChildB):
def __init__(self):
print("initializing Grandchild")
super().__init__()
Grandchild()
results in
initializing Grandchild
initializing ChildA
initializing Base
Replacing Base.__init__(self) with super().__init__() results in
initializing Grandchild
initializing ChildA
initializing ChildB
initializing Base
as desired.

some great answers here, but they do not tackle how to use super() in the case where different classes in the hierarchy have different signatures ... especially in the case of __init__
to answer that part and to be able to effectively use super() i'd suggest reading my answer super() and changing the signature of cooperative methods.
here's just the solution to this scenario:
the top-level classes in your hierarchy must inherit from a custom class like SuperObject:
if classes can take differing arguments, always pass all arguments you received on to the super function as keyword arguments, and, always accept **kwargs.
class SuperObject:
def __init__(self, **kwargs):
print('SuperObject')
mro = type(self).__mro__
assert mro[-1] is object
if mro[-2] is not SuperObject:
raise TypeError(
'all top-level classes in this hierarchy must inherit from SuperObject',
'the last class in the MRO should be SuperObject',
f'mro={[cls.__name__ for cls in mro]}'
)
# super().__init__ is guaranteed to be object.__init__
init = super().__init__
init()
example usage:
class A(SuperObject):
def __init__(self, **kwargs):
print("A")
super(A, self).__init__(**kwargs)
class B(SuperObject):
def __init__(self, **kwargs):
print("B")
super(B, self).__init__(**kwargs)
class C(A):
def __init__(self, age, **kwargs):
print("C",f"age={age}")
super(C, self).__init__(age=age, **kwargs)
class D(B):
def __init__(self, name, **kwargs):
print("D", f"name={name}")
super(D, self).__init__(name=name, **kwargs)
class E(C,D):
def __init__(self, name, age, *args, **kwargs):
print( "E", f"name={name}", f"age={age}")
super(E, self).__init__(name=name, age=age, *args, **kwargs)
E(name='python', age=28)
output:
E name=python age=28
C age=28
A
D name=python
B
SuperObject

Consider the following code:
class X():
def __init__(self):
print("X")
class Y(X):
def __init__(self):
# X.__init__(self)
super(Y, self).__init__()
print("Y")
class P(X):
def __init__(self):
super(P, self).__init__()
print("P")
class Q(Y, P):
def __init__(self):
super(Q, self).__init__()
print("Q")
Q()
If change constructor of Y to X.__init__, you will get:
X
Y
Q
But using super(Y, self).__init__(), you will get:
X
P
Y
Q
And P or Q may even be involved from another file which you don't know when you writing X and Y. So, basically, you won't know what super(Child, self) will reference to when you are writing class Y(X), even the signature of Y is as simple as Y(X). That's why super could be a better choice.

class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
This is fairly easy to understand.
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
Ok, what happens now if you use super(Child,self)?
When a Child instance is created, its MRO(Method Resolution Order) is in the order of (Child, SomeBaseClass, object) based on the inheritance. (assume SomeBaseClass doesn't have other parents except for the default object)
By passing Child, self, super searches in the MRO of the self instance, and return the proxy object next of Child, in this case it's SomeBaseClass, this object then invokes the __init__ method of SomeBaseClass. In other word, if it's super(SomeBaseClass,self), the proxy object that super returns would be object
For multi inheritance, the MRO could contain many classes, so basically super lets you decide where you want to start searching in the MRO.

Related

python object calling super on itself [duplicate]

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.
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 to InjectMe. Why isn't the conclusion "Always avoid using super"? 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.
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.
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
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)
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)
Super() in a nutshell
Every Python instance has a class that created it.
Every class in Python has a chain of ancestor classes.
A method using super() delegates work to the next ancestor in the chain for the instance's class.
Example
This small example covers all the interesting cases:
class A:
def m(self):
print('A')
class B(A):
def m(self):
print('B start')
super().m()
print('B end')
class C(A):
def m(self):
print('C start')
super().m()
print('C end')
class D(B, C):
def m(self):
print('D start')
super().m()
print('D end')
The exact order of calls is determined by the instance the method is called from:
>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()
For instance a, there is no super call:
>>> a.m()
A
For instance b, the ancestor chain is B -> A -> object:
>>> type(b).__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
>>> b.m()
B start
A
B end
For instance c, the ancestor chain is C -> A -> object:
>>> type(c).__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> c.m()
C start
A
C end
For instance d, the ancestor chain is more interesting D -> B -> C -> A -> object (mro stands for method resolution order) :
>>> type(d).__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> d.m()
D start
B start
C start
A
C end
B end
D end
More information
Having answered the question of "What does super do in Python?", the next question is how to use it effectively. See this step-by-step tutorial or this 45 minute video.
Many great answers, but for visual learners:
Firstly lets explore with arguments to super, and then without.
Imagine theres an instance jack created from the class Jack, who has the inheritance chain as shown in green in the picture. Calling:
super(Jack, jack).method(...)
will use the MRO (Method Resolution Order) of jack (its inheritance tree in a certain order), and will start searching from Jack. Why can one provide a parent class? Well if we start searching from the instance jack, it would find the instance method, the whole point is to find its parents method.
If one does not supply arguments to super, its like the first argument passed in is the class of self, and the second argument passed in is self. These are auto-calculated for you in Python3.
However say we dont want to use Jack's method, instead of passing in Jack, we could of passed in Jen to start searching upwards for the method from Jen.
It searches one layer at a time (width not depth), e.g. if Adam and Sue both have the required method, the one from Sue will be found first.
If Cain and Sue both had the required method, Cain's method would be called first.
This corresponds in code to:
Class Jen(Cain, Sue):
MRO is from left to right.
In the case of multiple inheritance, you normally want to call the initializers of both parents, not just the first. Instead of always using the base class, super() finds the class that is next in Method Resolution Order (MRO), and returns the current object as an instance of that class. For example:
class Base(object):
def __init__(self):
print("initializing Base")
class ChildA(Base):
def __init__(self):
print("initializing ChildA")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("initializing ChildB")
super().__init__()
class Grandchild(ChildA, ChildB):
def __init__(self):
print("initializing Grandchild")
super().__init__()
Grandchild()
results in
initializing Grandchild
initializing ChildA
initializing Base
Replacing Base.__init__(self) with super().__init__() results in
initializing Grandchild
initializing ChildA
initializing ChildB
initializing Base
as desired.
some great answers here, but they do not tackle how to use super() in the case where different classes in the hierarchy have different signatures ... especially in the case of __init__
to answer that part and to be able to effectively use super() i'd suggest reading my answer super() and changing the signature of cooperative methods.
here's just the solution to this scenario:
the top-level classes in your hierarchy must inherit from a custom class like SuperObject:
if classes can take differing arguments, always pass all arguments you received on to the super function as keyword arguments, and, always accept **kwargs.
class SuperObject:
def __init__(self, **kwargs):
print('SuperObject')
mro = type(self).__mro__
assert mro[-1] is object
if mro[-2] is not SuperObject:
raise TypeError(
'all top-level classes in this hierarchy must inherit from SuperObject',
'the last class in the MRO should be SuperObject',
f'mro={[cls.__name__ for cls in mro]}'
)
# super().__init__ is guaranteed to be object.__init__
init = super().__init__
init()
example usage:
class A(SuperObject):
def __init__(self, **kwargs):
print("A")
super(A, self).__init__(**kwargs)
class B(SuperObject):
def __init__(self, **kwargs):
print("B")
super(B, self).__init__(**kwargs)
class C(A):
def __init__(self, age, **kwargs):
print("C",f"age={age}")
super(C, self).__init__(age=age, **kwargs)
class D(B):
def __init__(self, name, **kwargs):
print("D", f"name={name}")
super(D, self).__init__(name=name, **kwargs)
class E(C,D):
def __init__(self, name, age, *args, **kwargs):
print( "E", f"name={name}", f"age={age}")
super(E, self).__init__(name=name, age=age, *args, **kwargs)
E(name='python', age=28)
output:
E name=python age=28
C age=28
A
D name=python
B
SuperObject
Consider the following code:
class X():
def __init__(self):
print("X")
class Y(X):
def __init__(self):
# X.__init__(self)
super(Y, self).__init__()
print("Y")
class P(X):
def __init__(self):
super(P, self).__init__()
print("P")
class Q(Y, P):
def __init__(self):
super(Q, self).__init__()
print("Q")
Q()
If change constructor of Y to X.__init__, you will get:
X
Y
Q
But using super(Y, self).__init__(), you will get:
X
P
Y
Q
And P or Q may even be involved from another file which you don't know when you writing X and Y. So, basically, you won't know what super(Child, self) will reference to when you are writing class Y(X), even the signature of Y is as simple as Y(X). That's why super could be a better choice.
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
This is fairly easy to understand.
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
Ok, what happens now if you use super(Child,self)?
When a Child instance is created, its MRO(Method Resolution Order) is in the order of (Child, SomeBaseClass, object) based on the inheritance. (assume SomeBaseClass doesn't have other parents except for the default object)
By passing Child, self, super searches in the MRO of the self instance, and return the proxy object next of Child, in this case it's SomeBaseClass, this object then invokes the __init__ method of SomeBaseClass. In other word, if it's super(SomeBaseClass,self), the proxy object that super returns would be object
For multi inheritance, the MRO could contain many classes, so basically super lets you decide where you want to start searching in the MRO.

Python - Demystifying the use of super() to call grandparent method

I can't seem to fully understand how super() really works when it comes to calling methods of a grandparent class. Suppose for instance we have the following code
class A():
def __init__(self):
print("initializing A")
def method(self):
print("method A")
class B(A):
def __init__(self):
print("initializing B")
def method(self):
print("method B")
class C(B):
def __init__(self):
print("initializing C")
def method(self):
super(B, self).method()
c = C()
c.method()
this will output
initializing C
method A
What I don't get is why the syntax (meaning super(Parent, self).method()) is like that, and more importantly what's going on under the hood. The Python official documentation (here) says
class super([type[, object-or-type]]) -
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 object-or-type determines the method resolution order to be searched. The search starts from the class right after the type.
Why does the type passed to super([type[, object-or-type]]) have to be the parent class B and not the grandparent class A? And how does passing self (in this case the instance of class C) help in determining the method resolution order to be searched?
The resolution order of C is [C, B, A, object]. When you use super, you are looking for the next class that provides the requested method. Inside C.method, super() and super(C, self) would be equivalent: look for the first definition of method in a class after C. When you pass B as the first argument, you are asking for the first definition of method in a class following B instead.
(The second argument, if not self, would be used to select an entirely different MRO, rather than just a different starting location for the search within the appropriate MRO.)
Use cases for other arguments to super are rare enough that it was worth adding special logic to the language itself to allow super() to be used in place explicit arguments. (They aren't just default function arguments, because the "default" values depend on the context in which super is called.)

Use class instance as parent class in python

I'm trying to get a better understanding of python class system. This question is intended only to satisfy my curiosity.
Is it possible to use somehow a class instance as a parent class for another class. So far what I tried to do is
class A:
pass
a = A()
class B(a):
pass
and it gives following error: TypeError: object() takes no parameters
class Meta(type):
pass
class A:
pass
a = A()
class B(a, metaclass=Meta):
pass
and it gives this error TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
I'm wondering, is it possible to somehow proxy all of the class instance methods to metaclass, so my instance would behave as a class
Is it possible to use somehow a class instance as a parent class for another class
Well, actually that's always the case since classes ARE instances (of their metaclass). But then the class of the instance you want to use as a parent class (yeah, re-read it thrice...) must behave as if it was a genuine metaclass - which can rapidily become impractical.
I'm wondering, is it possible to somehow proxy all of the class instance methods to metaclass, so my instance would behave as a class
There might be a way indeed (overriding __getattr__() or __getattribute__()), but devil is in the details and chances are that by the time you make this rube goldberg contraption "kind-of-work-with-severe-limitations-and-corner-cases", you will wonder if that was really worth the pain.
Note that OTHO both composition/delegation and dynamic (runtime) creation / modification of classes is absurdly easy in Python so I can hardly think of a problem that would be better solved by (ab)using an instance as a parent class.
Every instance has a __class__ attribute that refers to the underlying class structure.
Knowing this, you can actually do the following:
class A(object):
def foo(self):
return 'hello'
a = A()
class B(a.__class__):
pass
b = B()
print(b.foo()) # Prints 'hello'
Alternatively, you can also use type:
class B(type(a)):
pass
b = B()
print(b.foo()) # Prints 'hello'
Your instance needs to be a type in order to be used in inheritance chains. For example:
class A(type):
pass
a = A(str) # you need to pass the type's `type` to the construcor
class B(a):
pass
That being said, there's little, if no practical application for this that I can think of - or rather whatever you achieve this way will be easier and more Pythonic to achieve through normal inheritance and metaclasses.
EDIT:
OK, so it is possible to hack if you have control over the base class (either using metaclasses or __init_subclass__ (python 3.6 or later))
class A:
x = 1
def __init_subclass__(cls, inst, **kwargs):
super().__init_subclass__(**kwargs)
for k, v in vars(inst).items():
if k not in dir(cls): # i.e. don't override but remove if you want to
setattr(cls, k, v)
a = A()
a.y = 2
class B(A, inst=a):
pass
B.x # --> 1
B.y # --> 2
You're sort of on the right tracks with your second example. Yes it is possible.
class Meta(type): pass
class A(metaclass=Meta): pass
class B(A): pass
issubclass(B, A) # --> True
isinstance(B, Meta) # --> True
Thats because A is an instance of Meta (thats what a metaclass means, a class whose instance is also a class). Hence B is a subclass of an instance of Meta.
So yes, you could set for example
class Meta(type):
def __init__(cls, *args, **kwargs):
cls.x = 1
super().__init__(cls, *args, **kwargs)
class A(Meta):
def __init__(self):
self.y = 2
A.x # 1 (acts as an instance of Meta)
a = A()
a.y # 2 (acts as an instance of A)
a.x # AttributeError (does not act as an instance of Meta)

Inheritance in Python

I'm having a small amount of difficulty understanding inheritance in Python. I was under the impression that when Class B inherits from Class A it inherits all of the values that were initialized in A. I've made up an example to demonstrate what I mean:
Class A():
def __init__(self,parameter):
self.initialize_parameter=4*parameter
Class B(A):
def __init__(self):
pass
def function(self,another_parameter):
return self.initialize_parameter*another_parameter
But in this case, calling:
B_instance=B()
print B_instance.function(10)
Returns an AttributeError saying that class B doesn't not have self.initialized_parameter
So my question is, do I have to copy and paste all the initializations from A to B manually or is there a way that I can use them from within B without calling the A class itself? (I hope this is clear).
If you want to use the behaviour in the superclass' constructor you should either not override it:
Class B(A):
def function(self,another_parameter):
return self.initialize_parameter*another_parameter
Or if you want to extend the initialization behaviour you can use super()
Class B(A):
def __init__(self, parameter)
super(B, self).__init__(2*parameter)
def function(self,another_parameter):
return self.initialize_parameter*another_parameter
In Python 3 super() doesn't need explicit arguments (super().__init__()).
This general override behaviour and super() applies to all methods, not just constructors.
You have to use the super builtin in order to do the required initialisation for the parent class, like so:
class B(A):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
If you're not using Python 3, you might have to do it like this:
class B(A):
def __init__(self, *args, **kwargs):
super(A, self).__init__(*args, **kwargs)
Basically this calls A's __init__ function which is required if you want to reference A's methods and attributes.
The only way to initialize the base class is to use super().__init__() in B __init__() method. See: Understanding Python super() with __init__() methods
You are overriding the constructor from the super class here:
def __init__(self):
pass
So, self.initialize_parameter is never being initialized.
Delete that method, and it should work.
The __init__ of the superclass is only called automatically when you do not specify an __init__ in the subclass. So you should not define __init__ in class B unless you need to.
If you specify __init__ in class B you have to call __init__ in the superclass by hand. I prefer calling it this way: A.__init__(self,'provide_initialize_parameter_here'). Other people prefer using super.

What does 'super' do in Python? - difference between super().__init__() and explicit superclass __init__()

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.
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 to InjectMe. Why isn't the conclusion "Always avoid using super"? 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.
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.
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
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)
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)
Super() in a nutshell
Every Python instance has a class that created it.
Every class in Python has a chain of ancestor classes.
A method using super() delegates work to the next ancestor in the chain for the instance's class.
Example
This small example covers all the interesting cases:
class A:
def m(self):
print('A')
class B(A):
def m(self):
print('B start')
super().m()
print('B end')
class C(A):
def m(self):
print('C start')
super().m()
print('C end')
class D(B, C):
def m(self):
print('D start')
super().m()
print('D end')
The exact order of calls is determined by the instance the method is called from:
>>> a = A()
>>> b = B()
>>> c = C()
>>> d = D()
For instance a, there is no super call:
>>> a.m()
A
For instance b, the ancestor chain is B -> A -> object:
>>> type(b).__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
>>> b.m()
B start
A
B end
For instance c, the ancestor chain is C -> A -> object:
>>> type(c).__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> c.m()
C start
A
C end
For instance d, the ancestor chain is more interesting D -> B -> C -> A -> object (mro stands for method resolution order) :
>>> type(d).__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
>>> d.m()
D start
B start
C start
A
C end
B end
D end
More information
Having answered the question of "What does super do in Python?", the next question is how to use it effectively. See this step-by-step tutorial or this 45 minute video.
Many great answers, but for visual learners:
Firstly lets explore with arguments to super, and then without.
Imagine theres an instance jack created from the class Jack, who has the inheritance chain as shown in green in the picture. Calling:
super(Jack, jack).method(...)
will use the MRO (Method Resolution Order) of jack (its inheritance tree in a certain order), and will start searching from Jack. Why can one provide a parent class? Well if we start searching from the instance jack, it would find the instance method, the whole point is to find its parents method.
If one does not supply arguments to super, its like the first argument passed in is the class of self, and the second argument passed in is self. These are auto-calculated for you in Python3.
However say we dont want to use Jack's method, instead of passing in Jack, we could of passed in Jen to start searching upwards for the method from Jen.
It searches one layer at a time (width not depth), e.g. if Adam and Sue both have the required method, the one from Sue will be found first.
If Cain and Sue both had the required method, Cain's method would be called first.
This corresponds in code to:
Class Jen(Cain, Sue):
MRO is from left to right.
In the case of multiple inheritance, you normally want to call the initializers of both parents, not just the first. Instead of always using the base class, super() finds the class that is next in Method Resolution Order (MRO), and returns the current object as an instance of that class. For example:
class Base(object):
def __init__(self):
print("initializing Base")
class ChildA(Base):
def __init__(self):
print("initializing ChildA")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("initializing ChildB")
super().__init__()
class Grandchild(ChildA, ChildB):
def __init__(self):
print("initializing Grandchild")
super().__init__()
Grandchild()
results in
initializing Grandchild
initializing ChildA
initializing Base
Replacing Base.__init__(self) with super().__init__() results in
initializing Grandchild
initializing ChildA
initializing ChildB
initializing Base
as desired.
some great answers here, but they do not tackle how to use super() in the case where different classes in the hierarchy have different signatures ... especially in the case of __init__
to answer that part and to be able to effectively use super() i'd suggest reading my answer super() and changing the signature of cooperative methods.
here's just the solution to this scenario:
the top-level classes in your hierarchy must inherit from a custom class like SuperObject:
if classes can take differing arguments, always pass all arguments you received on to the super function as keyword arguments, and, always accept **kwargs.
class SuperObject:
def __init__(self, **kwargs):
print('SuperObject')
mro = type(self).__mro__
assert mro[-1] is object
if mro[-2] is not SuperObject:
raise TypeError(
'all top-level classes in this hierarchy must inherit from SuperObject',
'the last class in the MRO should be SuperObject',
f'mro={[cls.__name__ for cls in mro]}'
)
# super().__init__ is guaranteed to be object.__init__
init = super().__init__
init()
example usage:
class A(SuperObject):
def __init__(self, **kwargs):
print("A")
super(A, self).__init__(**kwargs)
class B(SuperObject):
def __init__(self, **kwargs):
print("B")
super(B, self).__init__(**kwargs)
class C(A):
def __init__(self, age, **kwargs):
print("C",f"age={age}")
super(C, self).__init__(age=age, **kwargs)
class D(B):
def __init__(self, name, **kwargs):
print("D", f"name={name}")
super(D, self).__init__(name=name, **kwargs)
class E(C,D):
def __init__(self, name, age, *args, **kwargs):
print( "E", f"name={name}", f"age={age}")
super(E, self).__init__(name=name, age=age, *args, **kwargs)
E(name='python', age=28)
output:
E name=python age=28
C age=28
A
D name=python
B
SuperObject
Consider the following code:
class X():
def __init__(self):
print("X")
class Y(X):
def __init__(self):
# X.__init__(self)
super(Y, self).__init__()
print("Y")
class P(X):
def __init__(self):
super(P, self).__init__()
print("P")
class Q(Y, P):
def __init__(self):
super(Q, self).__init__()
print("Q")
Q()
If change constructor of Y to X.__init__, you will get:
X
Y
Q
But using super(Y, self).__init__(), you will get:
X
P
Y
Q
And P or Q may even be involved from another file which you don't know when you writing X and Y. So, basically, you won't know what super(Child, self) will reference to when you are writing class Y(X), even the signature of Y is as simple as Y(X). That's why super could be a better choice.
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
This is fairly easy to understand.
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
Ok, what happens now if you use super(Child,self)?
When a Child instance is created, its MRO(Method Resolution Order) is in the order of (Child, SomeBaseClass, object) based on the inheritance. (assume SomeBaseClass doesn't have other parents except for the default object)
By passing Child, self, super searches in the MRO of the self instance, and return the proxy object next of Child, in this case it's SomeBaseClass, this object then invokes the __init__ method of SomeBaseClass. In other word, if it's super(SomeBaseClass,self), the proxy object that super returns would be object
For multi inheritance, the MRO could contain many classes, so basically super lets you decide where you want to start searching in the MRO.

Categories