Decorating Python class methods - how do I pass the instance to the decorator? Decorating Python class methods - how do I pass the instance to the decorator? python python

Decorating Python class methods - how do I pass the instance to the decorator?


You need to make the decorator into a descriptor -- either by ensuring its (meta)class has a __get__ method, or, way simpler, by using a decorator function instead of a decorator class (since functions are already descriptors). E.g.:

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

this prints

In decoin bar.foo

as desired.


Alex's answer suffices when a function is sufficient. 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. The descriptor protocol is the mechanism for binding a thing to an instance. It consists of __get__, __set__ and __delete__, which are called when the thing is got, set or deleted from the instances dictionary.

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


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

from functools import update_wrapper, partialclass MyDecorator(object):    def __init__(self, func):        update_wrapper(self, func)        self.func = func    def __get__(self, obj, objtype):        """Support instance methods."""        return partial(self.__call__, obj)    def __call__(self, obj, *args, **kwargs):        print('Logic here')        return self.func(obj, *args, **kwargs)my_decorator = MyDecoratorclass MyClass(object):     @my_decorator     def my_method(self):         pass