The Long and Short of Using =super
Using
super() in your python code makes your code more readable and easier to maintain. It also plays a critical role when multiple inheritance is involved to ensure that the multiple superclasses each get called. The way this works can be surprising and a bit complicated, so it's covered in detail in the
Long Notes on Super section below.
Short notes on Super
Overview
When subclassing, you often want to
extend the behavior of a method instead of
replacing it completely. Before
super you would simply do this by calling the mehod on your superclass directly from your method.
super allows you to make that method call in a more general way. When you use
super, you supply your own class name, and it figures out the appropriate superclass to be called.
To use super, you usually do something like this:
def aMethod(self, params):
super(classname, self).aMethod(params)
additions
where
classname is the name of your class,
params are the parameters to your method (minus self), and
additions is the extra behavoir you are adding to the method.
Usage Guidelines
In Chandler, our goal is to use super everywhere. An exception is where we're subclassing another code module that does not use super.
- You should always call
super if you are extending the functionality of a method. This means any __init__
or __del__
method should always call super.
- It's good practice to handle any additional arguments and pass them on, using
*args to pick up extra positional arguments, and **kwds to pick up additional keyword arguments. See below for usage examples.
- It is good practice not to need a return value (because of possible conflicts), but safest to return whatever super did (hopefully None), just in case.
- Usage of
super is important to making your code usable by others. Even though you may not be using multiple inheritance, and you know what your base classes are, you may still need to call super to make your code usable by subclasses. For example, you write a class that inherits directly from object. No need to call super, right? Wrong. Someone later subclasses off of your class, and they mixin an additional class. Now your code is being used in a multiple inheritance situation. If you failed to call super in your __init__
method, the __init__
method of their mixin class may never get called.
Trouble Shooting
- If you get an error that says something like:
super() argument 1 must be type, not classobj the problem is that you are using old-style python classes, which do not support super. The easy way to fix this is to put object at the base of your inheritance tree.
- Use your own classname as the first parameter to super, not the class you expect to call
- Pass all the arguments that came in to your method to your super's method call _except self_
- If none of your superclasses defines the method you are calling
super on, you will get an AttributeError. It's suggested that your wrap your call in a try block.
Long Notes on Super
When to call super
You have to make a judgement call when deciding if you should call
super or not. Are you overriding the method, or just overloading (extending the functionality)? Will the clients of your class want to extend the functionality that you are providing?
Where to place your super call
A lot of the time you want to call
super right at the beginning of your method. This is because the base class behavior is usually something that you build on top of, so you start with that. But there are many situations where you need to set the scene for the base class, so you put some functionality before the call to
super. Also, be aware that a lot could happen during the
super call, so any safe setup that you can do before calling
super can help prevent problems in complicated situations, e.g. when you invite recusion by defining
__getattr__
. In summary, there are no hard and fast rules, except to think about the potential implications of the code you are writing.
What happens with MI?
When multiple inheritance is involved, things get more complicated. Now the inheritance tree is not a simple hierarchy, and there is more than one root of the tree. It's important to be able to give control to each of the roots of the tree, so they can initialize their portion of the object, etc. The way
super does this is by linearizing the set of all superclasses into a single chain, and each call to
super goes one step farther along the chain. This may end up linking to root of one tree a leaf from the next tree. The code sample below illustrates the linkage between trees done by
super.
Code example
class M(object):
""" Mixin class """
def __init__(self, *args, **kwds):
print 'init.M'
super(M, self).__init__(*args, **kwds)
class B(object):
""" Base class """
def __init__(self, *args, **kwds):
print 'init.B'
super(B, self).__init__(*args, **kwds)
class D(B):
""" Derived class """
def __init__(self, *args, **kwds):
print 'init.D'
super(D, self).__init__(*args, **kwds)
class CL(M, D):
""" Class Left """
def __init__(self, *args, **kwds):
print 'init.CL'
super(CL, self).__init__(*args, **kwds)
class CR(D, M):
""" Class Right """
def __init__(self, *args, **kwds):
print 'init.CR'
super(CR, self).__init__(*args, **kwds)
When you execute
cl = CL() you get the following output:
init.CL
init.M
init.D
init.B
When you execute
cr = CR() you get:
init.CR
init.D
init.B
init.M
Note that in the first case M's call to
super is critical to allowing the rest of the init calls to happen, even though its superclass is
object. Equally important in the second case is B's call to
super, even though its superclass is
object.
Issues
Using super can be confusing. Hopefully this discussion, and other resources, help overcome this issue. Many of the newer books on Python do a good job addressing super, e.g.
Python in a Nutshell, pages 88-89.
Intermixing usage of super and direct-call-to-superclass is dangerous. (Direct-to-currently-expected-superclass calls may skip or even repeat some super classes because of the different MRO used by super.) Here's a link from the Twisted folks that addresses this point, and gives their perspective on super:
http://www.ai.mit.edu/people/jknight/super-harmful/
Sometimes calling super causes an error, because that method is not defined in any of your superclasses. As the example above illustrates, this can change when multiple inheritance is involved. If you think your class may be used in a multiple inheritance situation in the future, it makes sense to wrap the
super method call with a try block.
def f(self):
""" f method of my general base class """
print 'B'
try:
super(B, self).f()
except AttributeError:
pass # no f() in any super
--
DonnDenman - 26 Jul 2004
If you want to be extra careful, and not mask Exceptions raised by superclass methods, you need to create a temporary variable.
You may also want to return the result, for some methods.
def f(self):
""" f method of my general base class """
print 'B'
try:
# Get the next method, but don't call it yet
f=super(B, self).f
except AttributeError:
pass # no f() in any super
# Now actually call the next method *outside*
# of the except clause, in case *it* raises an
# AttributeError. If it has a return value,
# we might as well pass it on.
return f()