在python中修补绑定方法

时间:2016-07-20 15:47:15

标签: python

>>> class A:
...     def foo(self):
...             print(self)
...
>>>
>>> a = A()
>>> a.foo()
<__main__.A instance at 0x7f4399136cb0>
>>> def foo(self):
...     print(self)
...
>>> a.foo = foo
>>> a.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() takes exactly 1 argument (0 given)

我试图理解Python中的猴子修补。请说明错误原因以及解决方法。

2 个答案:

答案 0 :(得分:4)

this SO answer所述,您需要在执行此操作时使用types.MethodType或类似内容,例如:

a.foo = types.MethodType(foo, a)

原因是a.foo = foo只将函数foo设置为a的属性 - 没有&#34;绑定魔法&#34;已经完成了。让Python&#34;神奇地&#34;在调用a.foo时将实例作为第一个参数传递,您需要告诉Python进行这样的绑定,例如使用types.MethodType

有关(更多)详情,请参阅上述链接答案。

答案 1 :(得分:1)

所以这里棘手的问题是你得到的东西取决于方法所处的位置:

class A(object):
    def foo(self):
        print("Hello world")


def patch(self):
    print("patched!")


print(type(A.foo))
a = A()
print(type(a.foo))

如果你运行它,你会在python2.x和3.x上得到不同的结果:

$ python ~/sandbox/test.py  # python2.x
<type 'instancemethod'>
<type 'instancemethod'>
$ python3 ~/sandbox/test.py  # python3.x
<class 'function' at 0x100228020>
<class 'method' at 0x10021d0c0>

但在任何一种情况下,很明显a.foo是某种方法。

如果我们尝试修补它会发生什么?

a.foo = patch
print(type(a.foo))  # <type 'function'> (2.x) / <class 'function'> (3.x)

好的,现在我们看到a.foo的类型为function(不是方法)。所以问题是我们如何制定一个方法而不是“补丁”?答案是我们在将其添加为属性时使用它的描述符协议:

a.foo = patch.__get__(a, A)

对于类的方法,当你执行a.some_method时,python实际上执行:a.some_method.__get__(a, type(a))所以我们只是在这里(显式地)再现该调用序列。