Python 如何修补`\uuu调用``方法?

Python 如何修补`\uuu调用``方法?,python,monkeypatching,Python,Monkeypatching,我似乎无法对类实例的\uuuu调用\uuuu方法进行猴子式修补(是的,我只修补单个实例,而不是所有实例) 以下代码: class A(object): def test(self): return "TEST" def __call__(self): return "EXAMPLE" a = A() print("call method: {0}".format(a.__call__)) print("test method: {0}".for

我似乎无法对类实例的
\uuuu调用\uuuu
方法进行猴子式修补(是的,我只修补单个实例,而不是所有实例)

以下代码:

class A(object):
    def test(self):
        return "TEST"

    def __call__(self):
        return "EXAMPLE"

a = A()
print("call method: {0}".format(a.__call__))
print("test method: {0}".format(a.test))
a.__call__ = lambda : "example"
a.test = lambda : "test"
print("call method: {0}".format(a.__call__))
print("test method: {0}".format(a.test))

print(a())
print("Explicit call: {0}".format(a.__call__()))
print(a.test())
输出如下:

call method: <bound method A.__call__ of <__main__.A object at 0x7f3f2d60b6a0>>
test method: <bound method A.test of <__main__.A object at 0x7f3f2d60b6a0>>
call method: <function <lambda> at 0x7f3f2ef4ef28>
test method: <function <lambda> at 0x7f3f2d5f8f28>
EXAMPLE
Explicit call: example
test
如何调用monkeypatch
\uuuuuu()
?为什么我不能像修补其他方法一样修补它?

虽然说明了如何做(据推测,我还没有测试过),但没有解释问题的原因

对于自定义类,只有在对象的类型上定义了特殊方法的隐式调用,而不是在对象的实例字典中定义了特殊方法的隐式调用,才能保证其正常工作。该行为是以下代码引发异常的原因:

>>> class C:
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
>>C类:
...     通过
...
>>>c=c()
>>>c.。__len__;=λ:5
>>>莱恩(c)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:类型“C”的对象没有len()
来源:

因此,正如所评论的,Python真正做的是调用:

type(a).__call__(a)
同样地,如果我想覆盖
\uuuuu call\uuu
方法,我必须覆盖类的
\uuuu call\uuu
,但是如果我不想影响同一类的其他实例的行为,我需要使用被覆盖的
\uu call\uu
方法创建一个新类

因此,如何覆盖调用的示例如下所示:

class A(object):
    def test(self):
        return "TEST"

    def __call__(self):
        return "EXAMPLE"

def patch_call(instance, func):
    class _(type(instance)):
        def __call__(self, *arg, **kwarg):
           return func(*arg, **kwarg)
    instance.__class__ = _

a = A()
print("call method: {0}".format(a.__call__))
print("test method: {0}".format(a.test))
patch_call(a, lambda : "example")
a.test = lambda : "test"
print("call method: {0}".format(a.__call__))
print("test method: {0}".format(a.test))

print("{0}".format(a()))
print("Explicit a.__call__: {0}".format(a.__call__()))
print("{0}".format(a.test()))

print("Check instance of a: {0}".format(isinstance(a, A)))
运行它会产生以下输出:

call method: <bound method A.__call__ of <__main__.A object at 0x7f404217a5f8>>
test method: <bound method A.test of <__main__.A object at 0x7f404217a5f8>>
call method: <bound method patch_call.<locals>._.__call__ of <__main__.patch_call.<locals>._ object at 0x7f404217a5f8>>
test method: <function <lambda> at 0x7f404216d048>
example
Explicit a.__call__: example
test
Check instance of a: True 
调用方法:
试验方法:
调用方法:
试验方法:
例子
显式a..。\u调用:示例
测试
检查a的实例:True

您能让两个输出之间的2字符差异更明显一点吗?我盯着看了三分钟,试图找出其中的区别,但大多数人的大脑都会自动纠正一些小错误。告诉“a()不调用a.。\uuuuuuuuuuuuu。它调用类型(a)。\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?是否有一种方法可以使
isinstance(a,a)=True
即使在
\uuuu call\uuuuuu
被重写之后?是的,您可以将
a
子类化,如
类B(a):def\uu call\uuuuuuuuuuuuuuu(self):返回示例
。然后,您可以创建
B
的实例,而不是
A
,或者,如果您足够勇敢,可以创建
A.。\uuuuuu类\uuuuuuu=B
A.。\uuuuuuu类\uuuuuuuu=B
的实例实际上可以工作(我只需要覆盖基类的一些而不是所有实例的调用),让我来测试一下。如果一个._call_实际返回self.mycall(),会有什么不同吗?有时,我只是在后来决定让类可以直接调用时才这样做。然后你的monkey补丁就变成了一个标准补丁。@JLPeyret如果我在做
mycall()
我也可以在我使用它的地方调用
mycall()
。我认为,如果您了解Python使用神奇方法的行为,那么阅读它也会更加困难
call method: <bound method A.__call__ of <__main__.A object at 0x7f404217a5f8>>
test method: <bound method A.test of <__main__.A object at 0x7f404217a5f8>>
call method: <bound method patch_call.<locals>._.__call__ of <__main__.patch_call.<locals>._ object at 0x7f404217a5f8>>
test method: <function <lambda> at 0x7f404216d048>
example
Explicit a.__call__: example
test
Check instance of a: True