Recipe6.20.Using Cooperative Supercalls Concisely and Safely


Recipe 6.20. Using Cooperative Supercalls Concisely and Safely

Credit: Paul McNett, Alex Martelli

Problem

You appreciate the cooperative style of multiple-inheritance coding supported by the super built-in, but you wish you could use that style in a more terse and concise way.

Solution

A good solution is a mixin classa class you can multiply inherit from, that uses introspection to allow more terse coding:

import inspect class SuperMixin(object):     def super(cls, *args, **kwargs):         frame = inspect.currentframe(1)         self = frame.f_locals['self']         methodName = frame.f_code.co_name         method = getattr(super(cls, self), methodName, None)         if inspect.ismethod(method):             return method(*args, **kwargs)     super = classmethod(super)

Any class cls that inherits from class SuperMixin acquires a magic method named super: calling cls.super(args) from within a method named somename of class cls is a concise way to call super(cls, self).somename(args). Moreover, the call is safe even if no class that follows cls in Method Resolution Order (MRO) defines any method named somename.

Discussion

Here is a usage example:

if _ _name_ _ == '_ _main_ _':     class TestBase(list, SuperMixin):         # note: no myMethod defined here         pass     class MyTest1(TestBase):         def myMethod(self):             print "in MyTest1"             MyTest1.super( )     class MyTest2(TestBase):         def myMethod(self):             print "in MyTest2"             MyTest2.super( )     class MyTest(MyTest1, MyTest2):         def myMethod(self):             print "in MyTest"             MyTest.super( )     MyTest( ).myMethod( ) # emits: # in MyTest # in MyTest1 # in MyTest2

Python has been offering "new-style" classes for years, as a preferable alternative to the classic classes that you get by default. Classic classes exist only for backwards-compatibility with old versions of Python and are not recommended for new code. Among the advantages of new-style classes is the ease of calling superclass implementations of a method in a "cooperative" way that fully supports multiple inheritance, thanks to the super built-in.

Suppose you have a method in a new-style class cls, which needs to perform a task and then delegate the rest of the work to the superclass implementation of the same method. The code idiom is:

def somename(self, *args):     ...some preliminary task...     return super(cls, self).somename(*args)

This idiom suffers from two minor issues: it's slightly verbose, and it also depends on a superclass offering a method somename. If you want to make cls less coupled to other classes, and therefore more robust, by removing the dependency, the code gets even more verbose:

def somename(self, *args):     ...some preliminary task...     try:         super_method = super(cls, self).somename     except AttributeError:         return None     else:         return super_method(*args)

The mixin class SuperMixin shown in this recipe removes both issues. Just ensure cls inherits, directly or indirectly, from SuperMixin (alongside any other base classes you desire), and then you can code, concisely and robustly:

def somename(self, *args):     ...some preliminary task...     return cls.super(*args)

The classmethod SuperMixin.super relies on simple introspection to get the self object and the name of the method, then internally uses built-ins super and getattr to get the superclass method, and safely call it only if it exists. The introspection is performed through the handy inspect module of the standard Python library, making the whole task even simpler.

See Also

Library Reference and Python in a Nutshell docs on super, the new object model and MRO, the built-in getattr, and standard library module inspect; Recipe 20.12 for another recipe taking a very different approach to simplify the use of built-in super.



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