Welcome, guest | Sign In | My Account | Store | Cart

Makes cooperative calls looks nicer: super.method instead of super(cls,self).method .

Python, 39 lines
 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 ...

3 comments

Gonçalo Rodrigues 18 years, 4 months ago  # | flag

What if you need to spell out the class? I can rememer two places in my code where naming the class explicitly was helpful.

  1. 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.

  2. 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

    def get(self, inst):
        return getattr(super(cls, inst), self.attrib)
    

And variations thereof.

Gonçalo Rodrigues 18 years, 4 months ago  # | flag

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

class A(object):
    def method(self, args):
        ...

class B(A):
    def method(self, args):
        super(B, self).method(args)
        ...

class C(B)

    def method(self, args):
        #We want the grand-super (B), not super (C).
        super(B, self).method(args)
        ...

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:

class SuperGetter(object):
    def __init__(self, cls, attrib):
        self.cls = cls
        self.attrib = attrib

    def __call__(self, inst, args):
        func = getattr(super(cls, inst), attrib)
        return func(args)

And variations, thereof.

Michele Simionato (author) 18 years, 3 months ago  # | flag

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.

Created by Michele Simionato on Tue, 18 May 2004 (PSF)
Python recipes (4591)
Michele Simionato's recipes (12)

Required Modules

Other Information and Tasks