课堂内的装饰师&没有'自我'的装饰类方法给出了奇怪的结果

时间:2010-09-28 01:04:52

标签: python class decorator

示例代码:

# -*- 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)

这是正常的吗?有人可以解释一下吗?

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

2 个答案:

答案 0 :(得分:1)

这是完全正常的。

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

令你困惑的是什么?

值得注意的是,如果您使用call作为来自MyClass的装饰器,则会生成TypeError。解决这个问题的一种方法是将staticmethod装饰器应用于它,但是在课程构建期间你不能调用它。

这有点像hacky但我会解决如何同时使用它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的实例,原因与我上面解释的相同。具体来说,无论您明确传递给它的任何参数都来自之后隐式和自动放置在参数列表前面调用它的实例。

答案 1 :(得分:1)

@call
def myfunc(a):
    ...

相当于

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

orginial myfunc可能只期望一个参数a,但在使用call修饰后,新的myfunc可以获取任意数量的位置参数,并且他们都将被放入args

另请注意

def call(f)

从不致电f。所以

的事实
def myfunc(a)

缺乏正常的自我论证不是问题。它永远不会出现。

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

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

因此,你得到了

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