【问题标题】:How to monkeypatch one class's instance method to another one?如何将一个类实例方法monkeypatch到另一个?
【发布时间】:2013-09-10 07:36:33
【问题描述】:
给定一个类A,我可以简单地通过添加一个实例方法a
def a(self):
pass
A.a = a
但是,如果我尝试添加另一个类B 的实例方法b,即A.b = B.b,则尝试调用A().b() 会产生一个
TypeError: 未绑定的方法b() 必须以B 实例作为第一个参数调用(什么都没有)
(虽然B().b() 很好)。确实有区别
A.a -> <unbound method A.a>
A.b -> <unbound method B.b> # should be A.b, not B.b
所以,
- 如何解决这个问题?
- 为什么会这样?这似乎并不直观,但通常 Guido 有一些很好的理由......
奇怪的是,这在 Python3 中不再失败...
【问题讨论】:
标签:
python
python-2.x
monkeypatching
instance-methods
【解决方案1】:
让我们:
class A(object): pass
class B(object):
def b(self):
print 'self class: ' + self.__class__.__name__
当你在做的时候:
A.b = B.b
您不是将函数附加到A,而是未绑定的方法。因此,python 仅将其添加为标准属性,而不会将其转换为 A-unbounded 方法。解决方法很简单,附上底层函数:
A.b = B.b.__func__
print A.b
# print: <unbound method A.b>
a = A()
a.b()
# print: self class: A
我不知道未绑定方法和函数之间的所有区别(仅第一个包含第二个),也不知道所有这些在内部如何工作。所以我无法解释它的原因。我的理解是,一个方法对象(绑定与否)比函数需要更多的信息和功能,但它需要一个来执行。
我同意自动执行此操作(更改未绑定方法的类)可能是一个不错的选择,但我可以找到不这样做的理由。因此,python 3 与 python 2 的不同令人惊讶。我想找出这个选择的原因。
【解决方案2】:
当您引用类实例上的方法时,该方法将绑定到该类实例。
B().b 等价于:lambda *args, **kwargs: b(<B instance>, *args, **kwargs)
我怀疑您在评估 B.b. 时得到了类似(但不完全相同)的包装参考。然而,这不是我所期望的行为。
有趣的是:
A.a = lambda s: B.b(s)
A().a()
产量:
TypeError: 未绑定的方法 b() 必须首先以 B 实例调用
参数(取而代之的是 A 实例)
这表明B.b 正在评估实际方法的包装器,并且包装器正在检查“self”是否具有预期的类型。我不知道,但这可能与解释器的效率有关。
不过,这是一个有趣的问题。希望有人能给出更明确的答案。
【讨论】:
-
奇怪的是,在 Python3 上一切正常,其中(参见答案here)这个令人困惑的未绑定方法概念已被放弃
-
嗯,正如Juh_ 所示,只需使用B.b.__func__。仍然是一个奇怪的“功能”,难怪它不再在 Python3 中......