3

Python 的可调用对象是具有__call__方法的对象。它们大部分时间都是函数,但也可能是类实例。

但碰巧函数确实有__call__方法。因此,__call__方法也有__call__方法。以下 REPL 会话显示我们可以链接__call__s:

>>> print
<built-in function print>

>>> print.__call__
<method-wrapper '__call__' of builtin_function_or_method object at 0x0000025E2D597F78>

>>> print.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x0000025E2F631438>

>>> print.__call__.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x0000025E2F5F85F8>

>>> print.__call__.__call__.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x0000025E2F725DA0> 

... 等等。值得注意的是,所有这些方法都有不同的地址。此外,它们都有相同的行为:

>>> print("a")
a
>>> print.__call__("a")
a
>>> print.__call__.__call__("a")
a
>>> print.__call__.__call__.__call__("a")

那么,当我写的时候print("a"),实际上叫什么?是print,还是print.__call__?如果我Foo用方法定义一个类__call__怎么办?

此外,怎么可能每种__call__方法都有自己不同 __call__的方法?难道它们实际上是在我尝试访问它们时创建的吗?

4

2 回答 2

3

与类上的方法一样,__call__属性是在类型上定义的描述符对象,绑定到您查找的对象:

>>> type(print).__dict__['__call__']
<slot wrapper '__call__' of 'builtin_function_or_method' objects>
>>> type(print).__dict__['__call__'].__get__(print)
<method-wrapper '__call__' of builtin_function_or_method object at 0x10cc66f78>
>>> print.__call__
<method-wrapper '__call__' of builtin_function_or_method object at 0x10cc66f78>

绑定行为(通过__get__方法)是生成的method-wrapper实例如何知道将print对象作为传递self,就像将实例传递给您在自定义 Python 类上定义的方法一样。

所以是的,这意味着它们是按需创建的,因此将具有唯一的 ID。否则它们只是相同类型的更多实例。

于 2017-09-11T16:17:45.410 回答
2

print是类的一个实例builtin_function_or_method

>>> type(print)
builtin_function_or_method

众所周知(希望如此),当您访问方法时,实例会作为第一个参数隐式传递:

>>> type(print).__call__(print, 10)
10
>>> print.__call__(10)  # equivalent
10

这意味着当您访问__call__函数时,您实际上有类似绑定方法的东西。然而,绑定方法(或在本例中为 methodwrappers)也是 type 的实例method_wrapper

>>> type(print.__call__)
method-wrapper

因此,当您访问该__call__方法时,它又是一种“新”绑定方法。

>>> type(print.__call__).__call__(print.__call__, 10)  # equivalent
10

此时它变成递归的,因为方法包装器的方法只是另一个方法包装器实例:

>>> type(print.__call__.__call__)
method-wrapper

然而,它所做的一切只是创造了很多“不必要的”绑定方法......

于 2017-09-11T16:18:34.557 回答