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

Phil picture Phil · Mar 2, 2010 · Viewed 34.6k times · Source

This is Python 2.5, and it's GAE too, not that it matters.

I have the following code. I'm decorating the foo() method in 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 executing this I was hoping to see:

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

But I'm getting "TypeError: foo() takes exactly 1 argument (0 given)" as .foo(), being an object method, takes self as an argument. I'm guessing problem is that the instance of bar doesn't actually exist when I'm executing the decorator code.

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

Answer

Alex Martelli picture Alex Martelli · Mar 2, 2010

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 deco

class bar(object):
  @dec_check
  def foo(self):
    print 'in bar.foo'

b = bar()
b.foo()

this prints

In deco
in bar.foo

as desired.