【问题标题】:Decorating a method of a class causes an unbound method TypeError in 2.7装饰类的方法会导致 2.7 中的未绑定方法 TypeError
【发布时间】:2019-11-19 03:35:05
【问题描述】:

每当我在类定义之外装饰一个类的方法并使用它时,它都会抛出一个 TypeError 说以类实例作为第一个参数的未绑定方法。

我正在使用 setattr() 方法设置装饰方法。例如:

class A(object):

    @classmethod
    def demo_method(cls, a):
        print a

def decorator(function):

    from functools import wraps
    @wraps(function)
    def wrapper(*args, **kwargs):
        return_value = function(*args, **kwargs)
        return return_value

    return wrapper

setattr(A, 'demo_method', decorator(A.demo_method))

A.demo_method(1)

它会抛出以下错误:

TypeErrorTraceback (most recent call last)
<ipython-input-13-447ee51677f4> in <module>()
     17 setattr(A, 'demo_method', decorator(A.demo_method))
     18 
---> 19 A.demo_method(1)
     20 

TypeError: unbound method demo_method() must be called with A instance as first argument (got int instance instead)

【问题讨论】:

  • 适用于我的 Python 3.5:ideone.com/iPhNnk(但在 Python 2.7 上失败:ideone.com/3I0Qaz
  • 我正在使用 Python 2.7 @Adam.Er8。 2.7有什么解决方案吗?

标签: python python-2.7 python-decorators


【解决方案1】:

您看到的行为实际上与装饰器本身的使用无关。你也可以这样说:

class C(object):
    pass

setattr(C, 'm', lambda a: a)  # actually also just: C.m = lambda a: a
print(C.m(1))

而且您仍然会遇到基本相同的错误。您可以在 Python 2 文档中阅读更多关于 User-defined methods 的内容,即这些位:

请注意,每次从类或实例中检索属性时,都会发生从函数对象到(未绑定或绑定)方法对象的转换。 ...还要注意,这种转换只发生在用户定义的函数上;其他可调用对象(和所有不可调用对象)无需转换即可检索。同样重要的是要注意,作为类实例属性的用户定义函数不会转换为绑定方法;只有当函数是类的属性时才会发生这种情况。

您遇到的异常是因为:

当调用未绑定的用户定义方法对象时,会调用底层函数 (im_func),但第一个参数必须是正确类 (im_class) 或其派生类的实例。

如果您仔细查看C.m,您会看到:

>>> print(C.m)
<unbound method C.<lambda>>
>>> print(C().m)
<bound method C.<lambda> of <__main__.C object at 0x7f6f6c5fa850>>

而在 Python 3 中(正如评论中暗示的那样,此构造会通过),行为将是:

>>> print(C.m)
<function <lambda> at 0x7f69fbe8d0d0>
>>> print(C().m)
<bound method <lambda> of <__main__.C object at 0x7f69fbe89940>>

注意C.m 在后一种情况下仍然作为(普通)函数而不是(未绑定)方法访问。

我不完全确定您最终的目标是什么以及最有帮助的方向,但是为了获得与 Python 3 中此代码相同的行为,您可以更改(不是我会推荐它)C.m() 调用C.m.__func__() 直接访问底层函数。或者用你的例子:

A.demo_method.__func__(1)

【讨论】:

  • 感谢@Ondrej K 提供如此详细的解释
猜你喜欢
  • 2013-12-25
  • 2016-11-01
  • 2011-04-03
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
相关资源
最近更新 更多