Maybe a bit of example code will help: Notice the difference in the call signatures of foo
, class_foo
and static_foo
:
class A(object):
def foo(self, x):
print(f"executing foo({self}, {x})")
@classmethod
def class_foo(cls, x):
print(f"executing class_foo({cls}, {x})")
@staticmethod
def static_foo(x):
print(f"executing static_foo({x})")
a = A()
Below is the usual way an object instance calls a method. The object instance, a
, is implicitly passed as the first argument.
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>, 1)
With classmethods, the class of the object instance is implicitly passed as the first argument instead of self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)
You can also call class_foo
using the class. In fact, if you define something to be
a classmethod, it is probably because you intend to call it from the class rather than from a class instance. A.foo(1)
would have raised a TypeError, but A.class_foo(1)
works just fine:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)
One use people have found for class methods is to create inheritable alternative constructors.
With staticmethods, neither self
(the object instance) nor cls
(the class) is implicitly passed as the first argument. They behave like plain functions except that you can call them from an instance or the class:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
Staticmethods are used to group functions which have some logical connection with a class to the class.
foo
is just a function, but when you call a.foo
you don't just get the function,
you get a "partially applied" version of the function with the object instance a
bound as the first argument to the function. foo
expects 2 arguments, while a.foo
only expects 1 argument.
a
is bound to foo
. That is what is meant by the term "bound" below:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
With a.class_foo
, a
is not bound to class_foo
, rather the class A
is bound to class_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Here, with a staticmethod, even though it is a method, a.static_foo
just returns
a good 'ole function with no arguments bound. static_foo
expects 1 argument, and
a.static_foo
expects 1 argument too.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
And of course the same thing happens when you call static_foo
with the class A
instead.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
Best Solution
What's the difference?
means to call
SomeBaseClass
's__init__
. whilemeans to call a bound
__init__
from the parent class that followsSomeBaseClass
'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:
This only works in Python 3:
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 orcls
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, callingsuper
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:
Say you add another class to your object, and want to inject a class between Foo and Bar (for testing or some other reason):
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:
However, the class with the child that uses
super
can correctly inject the dependency:Addressing a comment
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 toThe
UnsuperChild
does not have access toInjectMe
. It is theUnsuperInjector
that has access toInjectMe
- and yet cannot call that class's method from the method it inherits fromUnsuperChild
.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.