函数调用和 __call__ 属性
Function call and the __call__ attribute
Python 的可调用对象是具有 __call__
方法的对象。
它们大部分时间是函数,但也可能是 class 个实例。
但碰巧函数确实有一个 __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__
?
如果我用 __call__
方法定义 Foo
class 会怎么样?
此外,每个__call__
方法怎么可能有自己的不同__call__
方法?
难道它们实际上是在我尝试访问它们时创建的?
与 classes 上的方法一样,__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 class.
上定义的方法中
是的,这意味着它们是按需创建的,因此将具有唯一的 ID。它们只是同一类型的更多实例。
print
是 class 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__
方法时,它又是一个 "new" 绑定方法。
>>> type(print.__call__).__call__(print.__call__, 10) # equivalent
10
此时它变为递归,因为方法包装器的方法只是另一个方法包装器实例:
>>> type(print.__call__.__call__)
method-wrapper
然而,所有这些只是创建了很多 "unnecessary" 绑定方法...
Python 的可调用对象是具有 __call__
方法的对象。
它们大部分时间是函数,但也可能是 class 个实例。
但碰巧函数确实有一个 __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__
?
如果我用 __call__
方法定义 Foo
class 会怎么样?
此外,每个__call__
方法怎么可能有自己的不同__call__
方法?
难道它们实际上是在我尝试访问它们时创建的?
与 classes 上的方法一样,__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 class.
是的,这意味着它们是按需创建的,因此将具有唯一的 ID。它们只是同一类型的更多实例。
print
是 class 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__
方法时,它又是一个 "new" 绑定方法。
>>> type(print.__call__).__call__(print.__call__, 10) # equivalent
10
此时它变为递归,因为方法包装器的方法只是另一个方法包装器实例:
>>> type(print.__call__.__call__)
method-wrapper
然而,所有这些只是创建了很多 "unnecessary" 绑定方法...