a question about decorator

Trent Nelson tnelson at onresolve.com
Mon Oct 22 09:31:07 CEST 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