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