【问题标题】:Decorating class methods - how to pass the instance to the decorator?装饰类方法 - 如何将实例传递给装饰器?
【发布时间】:2017-11-12 00:24:06
【问题描述】:

这是 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


    【解决方案1】:

    你需要把装饰器变成descriptor——要么确保它的(元)类有一个__get__方法,或者,方式更简单,使用装饰器 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
    

    根据需要。

    【讨论】:

    • f(self) 行更改为return f(self) 以将foo 的返回返回给调用者。
    • 描述符页面的链接断开。
    • @Apteryx:我链接了the Python docs on descriptors;干燥,但足够好地覆盖它们。
    • 如何通过这种实现将一些参数传递给装饰器?
    【解决方案2】:

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

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

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

    在这种情况下,当事物从实例中获取时,我们将其__call__ 方法的第一个参数绑定到实例,使用部分。这在构造类时对成员函数自动完成,但对于像这样的合成成员函数,我们需要显式地完成。

    【讨论】:

    • @Gilbert 描述符协议是将事物绑定到实例的机制。它由 __get __、__set __ 和 __delete __ 组成,当从实例字典中获取、设置或删除事物时会调用它们。在这种情况下,当事物从实例中获取时,我们将它的 __call __ 方法的第一个参数绑定到实例,使用部分。这是在构造类时对成员函数自动完成的,但是对于像这样的合成成员函数,我们需要显式地完成。
    • 那么如果装饰器接受参数呢?
    • python 文档有一个Descriptor HowTo Guide 部分
    • 更详细的解释代码:stackoverflow.com/questions/5469956/…
    • 实现__get__ 的更好方法是使用types.MethodType 来创建实际绑定(或在某些情况下在Py2 上,未绑定)方法对象。在 Py2 上,您将创建 __get__ 主体 return types.MethodType(self, instance, instancetype);在 Py3 上,您首先要在 instance 上测试 None 以避免绑定 (if instance is None: return self),否则,您将 return types.MethodType(self, instance)。完整示例here。将import 放在__get__ 之外; import(甚至缓存)对于简单的属性查找来说相对昂贵。
    【解决方案3】:

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

    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
    

    【讨论】:

    • 如何将参数传递给这个装饰器?我尝试将作为额外参数添加到 __init__ 方法,但是我真的不知道在实例化装饰器时为 func 传递什么:@my_decorator(func, given_arg)
    • @mhyousefi 你找到答案了吗?
    • @Ferus 老实说我不记得了。 :) 如果我这样做了,我会更新。
    • @mhyousefi 没问题我实际上想出了一个解决方案,通过添加构造函数 self.func = decorator()(self.func) 而不是添加装饰器。
    • 这个应该是最好的答案!!!
    猜你喜欢
    • 2019-02-03
    • 2019-05-24
    • 2013-03-10
    相关资源
    最近更新 更多