如何修补`__call__`方法?

时间:2016-07-23 11:02:10

标签: python monkeypatching

我似乎无法修补类实例的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

输出:

...
example
Explicit call: example
test

虽然我希望输出:

__call__()

我如何monkeypatch # templatetags/my_filters.py from django import template register = template.Library() @register.filter() def to_int(value): return int(value) ?为什么我不能像修补其他方法那样修补它?

虽然this answer告诉我们该怎么做(据说,我还没有测试过),但它并没有解释为什么部分问题。< / p>

2 个答案:

答案 0 :(得分:7)

所以,正如J.J. Hakala评论的那样,Python真正做的是调用:

type(a).__call__(a)

因此,如果我想覆盖__call__方法,我必须覆盖类的__call__,但如果我不想影响在同一个类的其他实例中,我需要使用覆盖__call__方法创建一个新类。

所以如何覆盖__call__的示例如下所示:

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 

答案 1 :(得分:4)

  

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

>>> 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()

来源:https://docs.python.org/3/reference/datamodel.html#special-lookup