Makes cooperative calls looks nicer: super.method instead of super(cls,self).method .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
"""Implements a nice syntax for cooperative methods. Could be made working for staticmethods and classmethods, but who needs them anyway? ;)""" import inspect def second_arg(func): args = inspect.getargspec(func) if len(args) >= 2: return args class _Cooperative(type): def __init__(cls, name, bases, dic): for n,func in dic.iteritems(): setattr(cls, n, func) def __setattr__(cls, name, func): set = super(_Cooperative, cls).__setattr__ if inspect.isfunction(func) and second_arg(func) == "super": set(name, lambda self, *args, **kw : func(self, super(cls, self), *args, **kw)) else: set(name, func) class Cooperative: __metaclass__ = _Cooperative if __name__ == "__main__": class B(Cooperative): def print_(self): print "B", class C(B): def print_(self, super): super.print_() print "C", class D(C): def print_(self, super): super.print_() print "D", D().print_() # => B C D
For the purpose of this recipe, cooperative methods are methods with a second argument called "super": super.method(args, *kw) is then a shortcut for super(cls,self).method(args, *kw). This avoids to repeat the name of the class in the super object, a thing I always loathed.
To use it, just import the Cooperative class and put it on top of your hierarchies. If you use multiple metaclasses, you may want to give a look to my recipe about removing metaclass conflicts.
I could make this recipe to work for staticmethods and classmethods, but I didn't bothered since I wanted to keep the cooperative module under the twenty lines.
This is just a hack, waiting for a nicer "super" built-in ...
What if you need to spell out the class? I can rememer two places in my code where naming the class explicitly was helpful.
In a class i needed to get not the super method but the grand-super method. I can't remember exactly why I needed it, but looking back, it must have been a case of bad design.
The following is not bad design, but a real need that came up:
class SuperGetter(object): def __init__(self, cls, attrib): super(Super, self).__init__(cls) self.__cls__ = cls self.__attrib__ = attrib
And variations thereof.
What if you need to spell out the class exactly? I have two use cases, where it actually was useful to be able to name the super class explicitly. One was, when I needed to get the grand-super implementation, that is
I can't remember the exact circumstances where I needed that, so quite possibly this was just a case of bad design. The following, I don't thing it is:
And variations, thereof.
Maybe I was unclear. I am not advocating replacing the current two-argument syntax of 'super'. We need it. I am just advocating for some syntactic sugar in the common cases where the first argument is the current class. No more than that.