Recipe20.12.Using Cooperative Super calls with Terser Syntax


Recipe 20.12. Using Cooperative Super calls with Terser Syntax

Credit: Michele Simionato, Gonçalo Rodrigues

Problem

You like 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 direct way.

Solution

A custom metaclass lets us selectively wrap the methods exposed by a class. Specifically, if the second argument of a method is named super, then that argument gets bound to the appropriate instance of the built-in super:

import inspect def second_arg(func):     args = inspect.getargspec(func)[0]     try: return args[1]     except IndexError: return None def super_wrapper(cls, func):     def wrapper(self, *args, **kw):         return func(self, super(cls, self), *args, **kw)     # 2.4 only: wrapper._ _name_ _ = func._ _name_ _     return wrapper class MetaCooperative(type):     def _ _init_ _(cls, name, bases, dic):         super(MetaCooperative, cls)._ _init_ _(cls, name, bases, dic)         for attr_name, func in dic.iteritems( ):             if inspect.isfunction(func) and second_arg(func) == "super":                 setattr(cls, attr_name, super_wrapper(cls, func))  class Cooperative:     _ _metaclass_ _ = MetaCooperative

Discussion

Here is a usage example of the custom metaclass presented in this recipe's Solution, in a typical toy case of "diamond-shaped" inheritance:

if _ _name_ _ == "_ _main_ _":     class B(Cooperative):         def say(self):             print "B",     class C(B):         def say(self, super):             super.say( )             print "C",     class D(B):         def say(self, super):             super.say( )             print "D",     class CD(C, D):         def say(self, super):             super.say( )             print '!'     CD( ).say( ) # emits: B D C !

Methods that want to access the super-instance just need to use super as the name of their second argument; the metaclass then arranges to wrap those methods so that the super-instance gets synthesized and passed in as the second argument, as needed.

In other words, when a class cls, whose metaclass is MetaCooperative, has methods whose second argument is named super, then, in those methods, any call of the form super.something(*args, **kw) is a shortcut for super(cls, self).something(*args, **kw). This approach avoids the need to pass the class object as an argument to the built-in super.

Class cls may also perfectly well have other methods that do not follow this convention, and in those methods, it may use the built-in super in the usual way: all it takes for any method to be "normal" is to not use super as the name of its second argument, surely not a major restriction. This recipe offers nicer syntax sugar for the common case of cooperative supercalls, where the first argument to super is the current classnothing more.

See Also

Library Reference and Python in a Nutshell docs on module inspect and the super built-in.



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