Decorating methods - where do my arguments go?

Peter Otten __peter__ at web.de
Fri May 8 12:39:29 EDT 2009


Mikael Olofsson 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?
> 
> /MiO

You have to turn your decorator into a descriptor by providing a __get__() 
method. A primitive example:

class test_decorator(object):
    def __init__(self,func):
        self._func = func
    def __call__(self, *args):
        print 'Decorator:', args
        self._func(self.inst, *args)
    def __get__(self, inst, cls):
        self.inst = inst
        return self

Read more about the details at

http://users.rcn.com/python/download/Descriptor.htm

Peter




More information about the Python-list mailing list