On May 8, 11:33 am, Mikael Olofsson <mik...@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 -- http://mail.python.org/mailman/listinfo/python-list