using super

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Mon Dec 31 11:03:32 EST 2007


En Mon, 31 Dec 2007 12:08:43 -0200, Steven D'Aprano  
<steve at REMOVE-THIS-cybersource.com.au> escribi�:

> On Mon, 31 Dec 2007 05:47:31 -0800, iu2 wrote:
>
>> I'm trying to make a method call automatically to its super using this
>> syntax:
>
>
> def chain(meth):  # A decorator for calling super.
>     def f(self, *args, **kwargs):
>         result = meth(self, *args, **kwargs)
>         S = super(self.__class__, self)
>         getattr(S, meth.__name__)(*args, **kwargs)
>         return result
>     f.__name__ = "chained_" + meth.__name__
>     return f
>
>
>
> class A(object):
>     def foo(self, x):
>         print "I am %s" % self
>         return x
>
> class B(A):
>     @chain
>     def foo(self, x):
>             print "This is B!!!"
>             return x + 1

If you later inherit from B and try to @chain a method, nasty things  
happen... The problem is that the two arguments to super (current class,  
and actual instance) are *both* required; you can't fake the first using  
self.__class__. But you can't refer to the current class inside the  
decorator, because it doesn't exist yet. You could use the decorator to  
just "mark" the function to be chained, and then -with the help of a  
metaclass- do the actual decoration when the class is created.

def chain(meth):
     """Mark a method to be "chained" later"""
     meth.chained = True
     return meth

def chain_impl(cls, meth):
     """The original decorator by SD'A"""
     def f(self, *args, **kwargs):
         result = meth(self, *args, **kwargs)
         S = super(cls, self)
         getattr(S, meth.__name__)(*args, **kwargs)
         return result
     f.__name__ = "chained_" + meth.__name__
     return f

class ChainedType(type):
     def __new__(meta, name, bases, dic):
         cls = super(ChainedType, meta).__new__(meta, name, bases, dic)
         # replace functions marked "to be chained" with its decorated  
version
         for name, value in dic.iteritems():
             if getattr(value, 'chained', False):
                 setattr(cls, name, chain_impl(cls, value))
         return cls

class A(object):
     __metaclass__ = ChainedType
     def foo(self, x):
         print "I am %s" % self
         return x

class B(A):
     @chain
     def foo(self, x):
         print "This is B!!!"
         return x + 1

class C(B):
     @chain
     def foo(self, x):
         print "This is C!!!"
         return x + 2

py> a = A()
py> a.foo(5)
I am <__main__.A object at 0x00A3C690>
5
py> b = B()
py> b.foo(5)
This is B!!!
I am <__main__.B object at 0x00A3CA90>
6
py> c = C()
py> c.foo(5)
This is C!!!
This is B!!!
I am <__main__.C object at 0x00A3C830>
7

The approach above tries to stay close to the chain decorator as  
originally posted. There are other ways that you can search for in the  
Python Cookbook.

-- 
Gabriel Genellina




More information about the Python-list mailing list