a question about decorator
Trent Nelson
tnelson at onresolve.com
Mon Oct 22 03:31:07 EDT 2007
> def A():
> print 'warp in A'
> def why(self, *arg, **kw):
> print 'in A'
> print self
> print arg
> print kw
> #self(*arg, **kw)
>
> return why
>
> class T(object):
> @A()
> def test(g, out):
> print 'in test', out
>
> it will out put:
>
> warp in A
> in A
> <function test at 0x00BF0C70>
> ()
> {}
>
> the function why will be called, why? there is no code to call it.
When Python first parses your code, every time it runs into '@A', it
calls A() in order to get the required decorator function. A needs to
return a function object that accepts a single parameter (the function
we're decorating), and returns another function object that does the
actual work. So, what you need is an extra level of inline functions:
def A(*args, **kwds):
# You can access the arguments passed to the decorator
# here, e.g. if we were called as @A('foo', timeout=10),
# args[0] would be 'foo', and kwds['timeout'] would be
# 10. *args and **kwds are also accessible in the actual
# decorator 'body' _fn() below. This allows you to alter
# the behaviour of the decorator based on the types that
# have been passed to it.
def _decorator(f):
# A decorator must always return a function object that
# takes a single parameter, which will be the function
# we're wrapping when we're actually invoked. That's
# the purpose of _decorator(f).
def _fn(*_args, **_kwds):
# And here's where we define the actual decorator
# functionality, three levels deep. *_args and
# **_kwds will represent the parameters passed to
# the actual function we're wrapping; i.e. in our
# case, _args[0] will always be self if we're
# decorating a class's instance method, and _args[1]
# will be either 'foo' or 'bar'.
self = _args[0]
name = self.__class__.__name__
print 'in A (calling class: %s)' % name
# The last responsibility of this method is to
# call the actual function we're wrapping.
f(*_args, **_kwds)
return _fn
return _decorator
class T(object):
@A()
def test(self, out):
print 'in test T', out
class S(object):
@A()
def test(self, out):
print 'in test S', out
if __name__ == '__main__':
t = T()
t.test('foo')
s = S()
s.test('bar')
% python t.py
in A (calling class: T)
in test T foo
in A (calling class: S)
in test S bar
Regards,
Trent.
More information about the Python-list
mailing list