Decorating Python class methods - how to pass an instance to a decorator?

This is Python 2.5, and it is GAE too, but it is not so important.

I have the following code. I decorate the foo () method in a bar using the dec_check class as a decorator.

class dec_check(object): def __init__(self, f): self.func = f def __call__(self): print 'In dec_check.__init__()' self.func() class bar(object): @dec_check def foo(self): print 'In bar.foo()' b = bar() b.foo() 

When doing this, I was hoping to see:

 In dec_check.__init__() In bar.foo() 

But I get " TypeError: foo() takes exactly 1 argument (0 given) " because .foo() , being an object method, takes self as an argument. I assume that the problem is that the bar instance does not actually exist when I execute the decorator code.

So, how do I pass the bar instance to the decorator class?

+58
python python-decorators
Mar 02 '10 at 18:38
source share
4 answers

You need to turn the decorator into a descriptor - either making sure that its (meta) class has the __get__ method, or, the way is simpler, using the decorator function instead of the decorator class (since functions are already descriptors). For example.:

 def dec_check(f): def deco(self): print 'In deco' f(self) return deco class bar(object): @dec_check def foo(self): print 'in bar.foo' b = bar() b.foo() 

it prints

 In deco in bar.foo 

optional.

+79
Mar 02
source share

Alex's answer is enough when the function is enough. However, when you need a class, you can make it work by adding the following method to the decorator class.

 def __get__(self, obj, objtype): """Support instance methods.""" import functools return functools.partial(self.__call__, obj) 

To understand this, you need to understand the descriptor protocol. A descriptor protocol is a mechanism for binding an object to an instance. It consists of __ get __, __set __ and __ delete __, which are called upon receipt, installation or removal of an object from the instance dictionary.

In this case, when the thing is obtained from the instance, we bind the first argument of its __call__ method to the instance using partial. This is done automatically for member functions when a class is created, but for a synthetic member function like this, we need to do this explicitly.

+44
Jul 21 '10 at 4:28
source share

Decorators can be a bit complicated in Python. Here is an example of what I ever came up with. You could conclude what you are trying to do.

Django and Ajax Authentication - URLs Requiring Login

+1
Mar 02 '10 at 18:45
source share

If you want to write a decorator as a class, you can do:

 from functools import update_wrapper, partial class MyDecorator(object): def __init__(self, func): update_wrapper(self, func) self.func = func def __get__(self, obj, objtype): """Support instance methods.""" return functools.partial(self.__call__, obj) def __call__(self, obj, *args, **kwargs): print('Logic here') return self.func(obj, *args, **kwargs) my_decorator = MyDecorator class MyClass(object): @my_decorator def my_method(self): pass 
0
Jul 27 '17 at 21:57
source share



All Articles