函数调用和__call__属性

时间:2017-09-11 16:09:09

标签: python callable-object

Python的callables是具有__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__方法怎么可能? 当我试图访问它们时,它们是否真的被创建了?

2 个答案:

答案 0 :(得分:2)

与类上的方法类似,__call__属性是在类型上定义的descriptor object,绑定到您查找的对象:

>>> 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。否则它们只是同一类型的更多实例。

答案 1 :(得分: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)也是类型method_wrapper

的实例
>>> type(print.__call__)
method-wrapper

因此,当您访问该版本的__call__方法时,它又会再次出现&#34; new&#34;约束方法。

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

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

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

然而,所有这一切只会造成很多不必要的&#34;约束方法......