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

问题描述 投票:0回答:3

这是 Python 2.5,也是 GAE,但这并不重要。

我有以下代码。我正在 bar 中装饰 foo() 方法,使用

dec_check
类作为装饰器。

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()

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

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

但是我得到

TypeError: foo() takes exactly 1 argument (0 given)
作为
.foo()
,作为一个对象方法,将
self
作为参数。我猜问题是,当我执行装饰器代码时,
bar
的实例实际上并不存在。

那么如何将

bar
的实例传递给装饰器类?

python python-decorators
3个回答
99
投票

你需要将装饰器变成一个描述符——要么确保它的(元)类有一个

__get__
方法,或者,way更简单,使用装饰器function而不是装饰器class (因为函数已经是描述符)。例如:

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()

这个打印

In deco
in bar.foo

随心所欲。


61
投票

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

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

要理解这一点,您需要了解描述符协议。描述符协议是将事物绑定到实例的机制。它由

__get__
__set__
__delete__
组成,当从实例字典中获取、设置或删除事物时会调用它们。

在这种情况下,当从实例获取事物时,我们使用partial将其

__call__
方法的第一个参数绑定到实例。对于成员函数,这是在构造类时自动完成的,但对于像这样的合成成员函数,我们需要显式地执行此操作。


11
投票

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

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
© www.soinside.com 2019 - 2024. All rights reserved.