装饰 class 的方法会导致 2.7 中的未绑定方法 TypeError
Decorating a method of a class causes an unbound method TypeError in 2.7
每当我在 class 定义之外修饰 class 的方法并使用它时,它会抛出一个 TypeError 提示未绑定方法,其中 class 实例作为第一个参数。
我正在使用 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)
您看到的行为实际上与装饰器本身的使用无关。你也可以这样说:
class C(object):
pass
setattr(C, 'm', lambda a: a) # actually also just: C.m = lambda a: a
print(C.m(1))
而且您仍然会遇到基本相同的错误。您可以在 User-defined methods 的 Python 2 文档中阅读更多内容,即这些位:
Note that the transformation from function object to (unbound or bound) method object happens each time the attribute is retrieved from the class or instance. ... Also notice that this transformation only happens for user-defined functions; other callable objects (and all non-callable objects) are retrieved without transformation. It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.
您遇到的异常是因为:
When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof.
如果您仔细查看 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)
每当我在 class 定义之外修饰 class 的方法并使用它时,它会抛出一个 TypeError 提示未绑定方法,其中 class 实例作为第一个参数。
我正在使用 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)
您看到的行为实际上与装饰器本身的使用无关。你也可以这样说:
class C(object):
pass
setattr(C, 'm', lambda a: a) # actually also just: C.m = lambda a: a
print(C.m(1))
而且您仍然会遇到基本相同的错误。您可以在 User-defined methods 的 Python 2 文档中阅读更多内容,即这些位:
Note that the transformation from function object to (unbound or bound) method object happens each time the attribute is retrieved from the class or instance. ... Also notice that this transformation only happens for user-defined functions; other callable objects (and all non-callable objects) are retrieved without transformation. It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.
您遇到的异常是因为:
When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof.
如果您仔细查看 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)