【问题标题】:decorator inside class & decorated classmethod without 'self' gives strange results类中的装饰器和没有“自我”的装饰类方法给出了奇怪的结果
【发布时间】:2010-09-28 01:04:52
【问题描述】:

示例代码:

# -*- coding: utf-8 -*-
from functools import wraps

class MyClass(object):
  def __init__(self):
    pass

  #decorator inside class
  def call(f):
    @wraps(f)
    def wrapper(*args):
      print 'Wrapper: ', args
    return wrapper

  #decorated 'method' without self
  @call
  def myfunc(a): 
    pass

c = MyClass()
c.myfunc(1)

返回:

Wrapper:  (<test3.MyClass object at 0xb788a34c>, 1)

这正常吗?谁能解释一下?

如果这是一个功能,我会在我的库中使用它。

【问题讨论】:

    标签: python class decorator


    【解决方案1】:

    这很正常。

    函数myfuncwrapper 的实例替换。 wrapper 的签名是(*args)。因为它是一个绑定方法,所以第一个参数是MyClass 的实例,它在字符串`Wrapper: '之后打印出来。

    什么让你困惑?

    值得注意的是,如果您使用call 作为MyClass外部 的装饰器,它将生成一个TypeError。解决此问题的一种方法是对其应用 staticmethod 装饰器,但随后您无法在类构建期间调用它。

    这有点老套,但我说明了如何同时拥有它here

    评论后更新

    无论您是否在参数列表中键入self,它都会将实例作为第一个参数,因为类创建之后,并且实例被实例化,它是一个绑定方法。当您以

    形式调用它时
    @instance.call
    def foo(bar):
        return bar + 1
    

    它扩展到

    def foo(bar):
        return bar + 1
    foo = instance.call(f)
    

    但请注意,您是在实例上调用它!这将自动扩展为形式的调用

    def foo(bar):
        return bar + 1
    foo = MyClass.call(instance, f)
    

    这就是方法的工作方式。但是你只定义了call 来接受一个参数,所以这会引发一个TypeError

    至于在类构造期间调用它,它工作得很好。但是它返回的函数在调用 it 时会传递一个 MyClass 的实例,原因与我上面解释的相同。具体来说,无论您显式传递给它的任何参数,都会在 将调用它的实例隐式和自动放置在参数列表的前面。

    【讨论】:

    • 我没有在 myfunc 中指定 'self',但装饰器在调用时得到了 'self' 值。如果我把装饰器放在课堂之外,它会失败(这是完全正常的:)。
    • 但也许你是对的,这是意料之中的。我会检查 'wraps' 的代码。
    • @bencevoltam wraps 与此无关!查看我的更新。
    【解决方案2】:
    @call
    def myfunc(a):
        ...
    

    等价于

    def myfunc(a):
        ...
    myfunc=call(myfunc)
    

    原来的myfunc 可能只期望一个参数a,但是在用call 修饰后,新的myfunc 可以接受任意数量的位置参数,它们都将放入@987654327 @。

    还要注意

    def call(f)
    

    永远不要打电话给f。所以事实是

    def myfunc(a)
    

    缺少正常的 self 参数不是问题。它永远不会出现。

    当您调用 c.myfunc(1) 时,wrapper(*args) 会被调用。

    什么是参数?好吧,由于c.myfunc 是一个方法调用,c 作为第一个参数发送,然后是任何后续参数。在这种情况下,后续参数为 1。两个参数都发送到 wrapper,因此 args 是 2 元组 (c,1)

    因此,你得到

    Wrapper:  (<test3.MyClass object at 0xb788a34c>, 1)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-14
      • 2020-01-11
      • 2012-02-09
      • 2021-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-06
      相关资源
      最近更新 更多