Decorators using instance variables

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Tue Aug 26 22:52:59 CEST 2008


Bruno Desthuilliers a écrit :
> robert2821 a écrit :

(snip - sorry, hit the 'send' button too soon)

>> def getdec(f):
>>    dec = decorator(f)
>>    return dec.docall
>>
> 
>> class decorator:
>>    def __init__ (self, f):
>>        self.f = f
>>
>>    def docall (self, *a):
>>        return self.f(*a)
>> 
>> class test:
>>    @getdec
>>    def doit (self, message):
>>        print message
>>
>> if __name__ == '__main__':
>>    foo = test ()
>>    foo.doit ('Hello, world')
>>

>>> I'm wondering if the following program should work. I think it
>>> should print 'Hello, World', but instead it produces a
 >>> TypeError. Is this a bug in decorators, a feature of them, or a
 >>> mistake or misunderstanding on my part?

<ot>
Last point : post the whole error message and traceback if possible
</ot>

Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
   File "/usr/tmp/python-10426amG.py", line 19, in <module>
     foo.doit ('Hello, world')
   File "/usr/tmp/python-10426amG.py", line 10, in docall
     return self. f(*a)
TypeError: doit() takes exactly 2 arguments (1 given)


This has to do with how Python makes methods from functions. Your getdec 
function - which is the real decorator - returns a class instance, not 
another function. This actually prevents doit() to be wrapped in a 
method when looked up on a test instance, so doit doesn't receive the 
instance as first param (hence the error message).

Here's a working version of your code using new-style classes:

def getdec(f):
     dec = Decorator(f)
     return dec

class Decorator(object):
     def __init__ (self, f):
         self.f = f
         self.instance = None

     def __get__(self, instance, cls):
         self.instance = instance
         return self

     def __call__(self, *args):
         args = (self.instance,) + args
         return self.f(*args)

class Test(object):
     @ getdec
     def doit(self, message):
         print message

if __name__ == '__main__':
     foo = Test ()

FWIW, note that the getdec function here is useless - you'd have the 
same result directly applying the Decorator class as a decorator.

HTH



More information about the Python-list mailing list