Decorating methods - where do my arguments go?
George Sakkis
george.sakkis at gmail.com
Sat May 9 04:37:32 EDT 2009
On May 8, 11:33 am, Mikael Olofsson <mik... at isy.liu.se> wrote:
> Hi all!
>
> I have long tried to avoid decorators, but now I find myself in a
> situation where I think they can help. I seem to be able to decorate
> functions, but I fail miserably when trying to decorate methods. The
> information I have been able to find on-line focuses on decorating
> functions, and do not mention any special problem regarding methods.
>
> Consider the following example. I start by defining a simple decorator:
>
> >>> class test_decorator(object):
> ... def __init__(self,func):
> ... self._func = func
> ... def __call__(self, *args):
> ... print 'Decorator:', args
> ... self._func(*args)
>
> Then I decorate a function:
>
> >>> @test_decorator
> ... def func(*args):
> ... print 'Function: ', args
>
> Let's try that:
>
> >>> func(1,2,3)
> Decorator: (1, 2, 3)
> Function: (1, 2, 3)
>
> OK! That was what I expected. Let's decorate a method:
>
> >>> class cls(object):
> ... @test_decorator
> ... def meth(self,*args):
> ... print 'Method: ', args
>
> Then try that:
>
> >>> cls().meth(1,2,3)
> Decorator: (1, 2, 3)
> Method: (2, 3)
>
> Oops! That's weird. I had expected - or at least wanted - the same
> result as for the function above.
>
> Where did the first argument go? If it was sent to the original method
> meth as the real first argument (self), then why did I not get an exception?
>
> More importantly: How do I write a decorator that does not drop arguments?
>
> I guess it all has to do with the fact that the returned callable isn't
> a method of cls. Is it possible to write a decorator that returns a
> callable that is a method of cls, when used on methods in cls?
Yes, just return an actual function from the decorator instead of a
callable object:
def test_decorator2(func):
def wrapper(*args):
print 'Decorator2:', args
func(*args)
return wrapper
class cls(object):
@test_decorator
def meth(self,*args):
print 'Method: ', args
@test_decorator2
def meth2(self,*args):
print 'Method2: ', args
>>> cls.meth
<__main__.test_decorator object at 0x87663cc>
>>> cls.meth2
<unbound method cls.wrapper>
>>> cls().meth2(1,2,3)
Decorator2: (<__main__.cls object at 0x8766ecc>, 1, 2, 3)
Method2: (1, 2, 3)
The reason this works is that functions are already descriptors, so
you don't have to explicitly define __get__() as you would for a new
callable class (see Peter's example).
HTH,
George
More information about the Python-list
mailing list