What does 'super' do in 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.
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.
What's the difference?
SomeBaseClass.__init__(self)
means to call SomeBaseClass
's __init__
. while
super(Child, self).__init__()
means to call a bound __init__
from the parent class that follows Child
in the instance's method resolution order (MRO).
If the instance is a subclass of Child, there may be a different parent that comes next in the MRO.
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 (eg Child
) in the free variables (it is looked up with the name __class__
as a free closure variable in the method).
I prefer to demonstrate the cross-compatible way of using super
, but if you are only using Python 3, you can call it 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. Particularly in Python 2, getting the arguments to super
and the correct method arguments right can be difficult. 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(SuperChild, self).__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(InjectMe, self).__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
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.
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, ie: class A(object)
上一篇: 在Firefox扩展中改变HTTP响应
下一篇: “超级”在Python中做什么?