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)[0]
if len(args) >= 2: return args[1]
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.