装饰Python类方法 - 如何将实例传递给装饰器?

Phi*_*hil 59 python python-decorators

这是Python 2.5,它也是GAE,并不重要.

我有以下代码.我正在使用dec_check类作为装饰器在bar中装饰foo()方法.

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()
Run Code Online (Sandbox Code Playgroud)

执行此操作时,我希望看到:

In dec_check.__init__()
In bar.foo()
Run Code Online (Sandbox Code Playgroud)

但我得到" TypeError: foo() takes exactly 1 argument (0 given)"作为.foo()一种对象方法,以自我为参数.我猜测问题是bar当我执行装饰器代码时,实例并不存在.

那么如何将一个实例传递bar给装饰器类呢?

Ale*_*lli 81

你需要让装饰成一个描述符 -无论是确保其(元)类有一个__get__方法,或者,方式简单,通过使用装饰功能,而不是装饰(因为功能已经描述符).例如:

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()
Run Code Online (Sandbox Code Playgroud)

这打印

In deco
in bar.foo
Run Code Online (Sandbox Code Playgroud)

如预期的.

  • 将`f(self)`行更改为`return f(self)`以将`foo`的返回传递给调用者. (2认同)
  • 链接到描述符页面的链接断开。 (2认同)

Gor*_*ley 46

当函数足够时,Alex的答案就足够了.但是,当您需要一个类时,可以通过将以下方法添加到装饰器类来使其工作.

def __get__(self, obj, objtype):
    """Support instance methods."""
    import functools
    return functools.partial(self.__call__, obj)
Run Code Online (Sandbox Code Playgroud)

要理解这一点,您需要了解描述符协议.描述符协议是将事物绑定到实例的机制.它由__ get __,__ set __和__ delete __组成,当从实例字典中获取,设置或删除事物时调用它们.

在这种情况下,当从实例获取东西时,我们使用partial将其__ call __方法的第一个参数绑定到实例.在构造类时,这是为成员函数自动完成的,但对于像这样的合成成员函数,我们需要显式地完成它.

  • @Gilbert描述符协议是将事物绑定到实例的机制.它由__ get __,__ set __和__ delete __组成,当从实例字典中获取,设置或删除事物时调用它们.在这种情况下,当从实例获取东西时,我们使用partial将它的__ call __方法的第一个参数绑定到实例.在构造类时,这是为成员函数自动完成的,但对于像这样的合成成员函数,我们需要显式地完成它. (6认同)
  • 这似乎有效.你能解释一下这是如何工作的吗? (4认同)
  • 那么装饰者是否接受了争论呢? (4认同)
  • 实现 `__get__` 的更好方法是使用 `types.MethodType` 来创建一个实际的绑定(或者在某些情况下,在 Py2 上,未绑定)方法对象。在 Py2 上,您可以将 `__get__` 主体设置为 `return types.MethodType(self, instance, instancetype)`;在 Py3 上,您首先要在“instance”上测试“None”以避免绑定(“如果实例为 None:返回 self”),否则,您将“返回 types.MethodType(self, instance)”。完整示例[此处](/sf/answers/4046615471/)。不过,将“import”放在“__get__”之外;对于简单的属性查找来说,“导入”(甚至缓存)的成本相对较高。 (2认同)

Tho*_*ner 12

如果你想将装饰器编写为一个类,你可以这样做:

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 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
Run Code Online (Sandbox Code Playgroud)

  • 如何将参数传递给这个装饰器?我尝试将额外参数添加到 __init__ 方法中,但后来我真的不知道在实例化装饰器时要为 func 传递什么:@my_decorator(func,given_arg)。 (4认同)