Recipe6.19.Calling a Superclass _ _init_ _ Method If It Exists


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):     def _ _init_ _(self):         for base in self_ _class_ _._ _bases_ _:             if hasattr(base, '_ _init_ _'):                 base._ _init_ _(self)         initialization specific to subclass LookBeforeYouLeap

More generally, and not just for method _ _init_ _, 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):     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( )

This snippet emits:

met in Base1 met in Der1 met in Der2

The 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 inspect 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'

inspect.getargspec raises 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.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net