【问题标题】:Monkey-patching bound methods in python [duplicate]python中的猴子修补绑定方法[重复]
【发布时间】:2016-11-23 22:02:15
【问题描述】:
>>> 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 中的猴子补丁。请说明错误原因及解决方法。

【问题讨论】:

    标签: python


    【解决方案1】:

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

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

    原因是a.foo = foo 只是将函数foo 设置为a 的属性——没有完成“绑定魔法”。要让 Python 在调用 a.foo 时“神奇地”将实例作为第一个参数传递,您需要告诉 Python 进行此类绑定,例如通过使用types.MethodType

    有关(更多)详细信息,请参阅上面链接的答案。

    【讨论】:

      【解决方案2】:

      所以这里的棘手之处在于,你得到什么取决于方法所在的位置:

      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)) 所以我们只是在这里(显式地)重现该调用序列。

      【讨论】:

      • 谢谢!在任何情况下,猴子补丁都不需要描述符协议。在这个例子中,github.com/yasoob/intermediatePython/blob/master/… 这个人没有使用它,它仍然有效。
      • @AbhishekBhatia -- 如果在创建实例之前修补类,则不需要它:A.foo = patch; a = A(); a.foo() -- 但是,如果您真的要进行大量修补,我希望它只是在测试中,我建议你使用一个模拟库来做它(例如mock
      • 谢谢!你能解释一下为什么你建议创建一个单独的库吗?不确定我是否理解你。
      • @AbhishekBhatia -- 不是创建使用 :-)。原因是他们可能已经为你完成了大部分艰苦的工作。我建议unittest.mock(如果您使用的是python2.x,可以使用pip 下载/安装)。显然,了解这些东西在需要它们的场合是如何工作的很好,但大多数时候,您可以使用该库而不必担心细节。
      猜你喜欢
      • 2015-03-23
      • 2017-02-12
      • 2019-03-29
      • 1970-01-01
      • 2021-03-10
      • 2016-03-26
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多