Recipe 6.19. Calling a Superclass _ _init_ _ Method If It Exists
Credit: Alex Martelli
Problem
You want to ensure that _ _init_ _ is called for
all superclasses that define it, and Python does not do this
automatically.
Solution
As long as your class is new-style, the built-in
super makes this task easy (if all
superclasses' _ _init_ _ methods
also use super similarly):
class NewStyleOnly(A, B, C):
def _ _init_ _(self):
super(NewStyleOnly, self)._ _init_ _( )
initialization specific to subclass NewStyleOnly
Discussion
Classic classes are
not recommended for new code development: they
exist only to guarantee backwards compatibility with old versions of
Python. Use new-style classes (deriving directly or indirectly from
object) for all new code. The only thing you
cannot do with a new-style class is to raise its
instances as exception objects; exception classes must therefore be
old style, but then, you do not need the functionality of this recipe
for such classes. Since the rest of this recipe's
Discussion is therefore both advanced and of limited applicability,
you may want to skip it.Still, it may happen that you need to retrofit this functionality
into a classic class, or, more likely, into a new-style class with
some superclasses that do not follow the proper
style of cooperative superclass method-calling with the built-in
super. In such cases, you should first try to fix
the problematic premisesmake all classes new style and make
them use super properly. If you absolutely cannot
fix things, the best you can do is to have your class loop over its
base classesfor each base, check whether it has an _
_init_ _, and if so, then call it:
class LookBeforeYouLeap(X, Y, Z):More generally, and not just for method _ _init_
def _ _init_ _(self):
for base in self_ _class_ _._ _bases_ _:
if hasattr(base, '_ _init_ _'):
base._ _init_ _(self)
initialization specific to subclass LookBeforeYouLeap
_, we often want to call a method on an instance, or class,
if and only if that method exists; if the method does not exist on
that class or instance, we do nothing, or we default to another
action. The technique shown in the
"Solution", based on built-in
super, is not applicable in general: it only works
on superclasses of the current object, only if those superclasses
also use super appropriately, and only if the
method in question does exist in some superclass. Note that all
new-style classes do have an _ _init_ _ method:
they all subclass object, and
object defines _ _init_ _ (as a
do-nothing function that accepts and ignores any arguments).
Therefore, all new-style classes have an _ _init_
_ method, either by inheritance or by override.The
LBYL technique shown in class LookBeforeYouLeap may
be of help in more general cases, including ones that involve methods
other than _ _init_ _. Indeed, LBYL may even be
used together with super, for
example, as in the following toy example:
class Base1(object):This snippet emits:
def met(self):
print 'met in Base1'
class Der1(Base1):
def met(self):
s = super(Der1, self)
if hasattr(s, 'met'):
s.met( )
print 'met in Der1'
class Base2(object):
pass
class Der2(Base2):
def met(self):
s = super(Der2, self)
if hasattr(s, 'met'):
s.met( )
print 'met in Der2'
Der1( ).met( )
Der2( ).met( )
met in Base1The
met in Der1
met in Der2
implementation of met has the same structure in both
derived classes, Der1 (whose superclass
Base1 does have a method named
met) and Der2 (whose superclass
Base1 doesn't
have such a method). By binding a local name
s to the result of
super, and checking with
hasattr that the superclass does have such a
method before calling it, this LBYL structure lets you code in the
same way in both cases. Of course, when coding a subclass, you
do normally know which methods the superclasses
have, and whether and how you need to call them. Still, this
technique can provide a little extra flexibility for those occasions
in which you need to slightly decouple the subclass from the
superclass.The LBYL technique is far from perfect, though: a superclass might
define an attribute named met, which is not callable
or needs a different number of arguments. If your need for
flexibility is so extreme that you must ward against such
occurrences, you can extract the superclass' method
object (if any) and check it with the getargspec
function of standard library module inspect.While pushing this idea towards full generality can lead into rather
deep complications, here is one example of how you might code a class
with a method that calls the superclass' version of
the same method only if the latter is callable without arguments:
import inspectinspect.getargspec raises
class Der(A, B, C, D):
def met(self):
s = super(Der, self)
# get the superclass's bound-method object, or else None
m = getattr(s, 'met', None)
try:
args, varargs, varkw, defaults = inspect.getargspec(m)
except TypeError:
# m is not a method, just ignore it
pass
else:
# m is a method, do all its arguments have default values?
if len(defaults) == len(args):
# yes! so, call it:
m( )
print 'met in Der'
a TypeError if its argument is not a method or
function, so we catch that case with a TRy/except
statement, and if the exception occurs, we just ignore it with a
do-nothing pass statement in the
except clause. To simplify our code a bit, we do
not first check separately with hasattr. Rather,
we get the 'met' attribute of the superclass by
calling getattr with a third argument of
None. Thus, if the superclass does not have any
attribute named 'met', m is set
to None, later causing exactly the same
TypeError that we have to catch (and ignore)
anywaytwo birds with one stone. If the call to
inspect.getargspec in the try
clause does not raise a TypeError, execution
continues with the else clause.If inspect.getargspec doesn't
raise a TypeError, it returns a tuple of four
items, and we bind each item to a local name. In this case, the ones
we care about are args, a list of
m's argument names, and
defaults, a tuple of default values that
m provides for its arguments. Clearly, we can call
m without arguments if and only if
m provides default values for all of its
arguments. So, we check that there are just as many default values as
arguments, by comparing the lengths of list
args and tuple defaults, and call
m only if the lengths are equal.No doubt you don't need such advanced introspection
and such careful checking in most of the code you write, but, just in
case you do, Python does supply all the tools you need to achieve it.
See Also
Docs for built-in functions super,
getattr, and hasattr, and
module inspect, in the Library
Reference and Python in a
Nutshell.