Decorating methods - where do my arguments go?

Duncan Booth duncan.booth at invalid.invalid
Tue May 12 04:25:59 EDT 2009


Michele Simionato <michele.simionato at gmail.com> wrote:

> On May 12, 8:54 am, Mikael Olofsson <mik... at isy.liu.se> wrote:
>> Peter Otten wrote:
>> > I usually use decorator functions, but I think the following should
>> > wor 
> k,
>> > too:
>>
>> > class deco(object):
>> >     def __init__(self, func):
>> >         self._func = func
>> >     def __call__(self, *args):
>> >         print "Decorator:", args
>> >         self._func(*args)
>> >     def __get__(self, *args):
>> >         return deco(self._func.__get__(*args))
>>
>> This looks like a winner to me. It is elegant, works for functions as
>> well as for methods, and does not mess with the internals of the
>> decorator which avoids the problems that Duncan pointed out.
> 
> Still it turns something which is a function into an object and you
> lose the
> docstring and the signature. pydoc will not be too happy with this
> approach.

Also it means every time the decorator is invoked 'self' references a 
new instance of deco. I don't know why Mikael wants to use a class 
rather than a function but if he wants to save state between calls this 
isn't going to help.

Something like this may work, and preserves the function name, type and 
docstring:

from functools import wraps
class Decorator(object):
    def __init__(self, func):
        self._func = func

    def __new__(cls, func):
        decorator = object.__new__(cls)
        decorator.__init__(func)
        @wraps(func)
        def wrapper(*args, **kwds):
            return decorator(*args, **kwds)
        return wrapper

class deco(Decorator):
    def __init__(self, func):
        self.calls = 0
        Decorator.__init__(self, func)
        
    def __call__(self, *args):
        self.calls += 1
        print "Decorator:", args, "called", self.calls, "times"
        return self._func(*args)

class C(object):
    @deco
    def meth(self, arg):
        """meth's docstring"""
        print "meth %r %r" % (self, arg)
        return arg+1

inst1 = C()
inst2 = C()
print repr(inst1.meth), inst1.meth.__doc__

print inst1.meth(5)
print inst1.meth(inst2.meth(3))

-------------------
Output:
<bound method C.meth of <__main__.C object at 0x00B45390>> meth's 
docstring
Decorator: (<__main__.C object at 0x00B45390>, 5) called 1 times
meth <__main__.C object at 0x00B45390> 5
6
Decorator: (<__main__.C object at 0x00B453D0>, 3) called 2 times
meth <__main__.C object at 0x00B453D0> 3
Decorator: (<__main__.C object at 0x00B45390>, 4) called 3 times
meth <__main__.C object at 0x00B45390> 4
5


-- 
Duncan Booth http://kupuguy.blogspot.com



More information about the Python-list mailing list