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

落花浮王杯 提交于 2019-11-26 04:37:08

问题


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?


回答1:


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.




回答2:


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.




回答3:


Decorators can be a little tricky in Python. Here's an example of one I got to work sometime back. You might be able to infer from it what you're trying to do.

Django authentication and Ajax - URLs that require login




回答4:


If you want to write the 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


来源:https://stackoverflow.com/questions/2365701/decorating-python-class-methods-how-do-i-pass-the-instance-to-the-decorator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!