__call__ 是如何运作的?
How does __call__ actually work?
Python 的魔术方法 __call__
会在您尝试调用对象时被调用。 Cls()()
因此等于 Cls.__call__(Cls())
.
函数是 Python 中的第一个 class 对象,这意味着它们只是可调用对象(使用 __call__
)。但是,__call__
本身是一个函数,因此它也有 __call__
,它又有它自己的 __call__
,它又有它自己的 __call__
。
因此 Cls.__call__(Cls())
等于 Cls.__call__.__call__(Cls())
并再次等于 Cls.__call__.__call__.__call__(Cls())
等等。
这个无限循环是如何结束的? __call__
实际上是如何执行代码的?
没有真正的无限循环,因为在所有这些情况下 __call__
方法实际上并未被调用 ("called")。只有在对提供 __call__
方法的对象进行类似函数的调用时才会直接调用它。
正常 class 实例化 Cls(...)
和常规函数调用 f()
是直接处理的已知情况。通常没有 __call__()
的实际调用,因此可能会发生有限数量的 __call__
方法调用,即使在具有深度继承、metaclasses 等的复杂情况下也是如此.
因为对于概念无限循环的短路是否真的发生了一些争议,让我们看一下反汇编的字节码。考虑以下代码:
def f(x):
return x + 1
class Adder(object):
def something(self, x):
return x + 19
def __call__(self, x):
return x + 1
def lotsacalls(y):
u = f(1)
a = Adder()
z = u + a.something(y)
return a(z * 10)
抱歉,它有点复杂,因为我想展示几个短路实例——即普通 def
函数、__init__
调用、普通方法和 __call__
特殊方法。现在:
所以这里有一个时间范围,如果 Python 真的,真的 "walking the tree" 概念性 __call__
调用,它将引用 Function
(并且可能Method
classes,并调用它们的 __call__
方法)。它没有。它在所有情况下都使用简单的字节码 CALL_FUNCTION
,从而缩短了概念树的遍历。 逻辑上 你可以想象有一个 class Function
有一个 __call__
方法,当一个函数(即 Function
class) 被调用。但事实并非如此。编译器、字节码解释器和 C 语言基础的其他部分 实际上 遍历元 class 树。他们疯狂地短路。
我没有检查任何文档,但从我的测试看来 __call__
并不总是被调用:
def func1(*args, **kargs):
print "func1 called", args, kargs
def func2(*args, **kargs):
print "func2 called", args, kargs
func1.__call__ = func2
func1() # here is still called func1
class Cls:
def __init__(*args, **kargs):
print "init called", args, kargs
def __call__(*args, **kargs):
print "object called", args, kargs
obj = Cls() # here is actually called __init__
obj() # here is called __call__
这会打印
func1 called () {}
init called (<__main__.Cls instance at 0x0000000002A5ED88>,) {}
object called (<__main__.Cls instance at 0x0000000002A5ED88>,) {}
在幕后,Python 中的所有调用都使用相同的机制,并且几乎所有调用都到达 CPython 实现中的相同 C 函数。无论对象是具有 __call__
方法的 class 的实例、函数(本身是对象)还是内置对象,所有调用(优化的特殊情况除外)都会到达函数 PyObject_Call
。该 C 函数从对象的 PyObject
结构的 ob_type
字段获取对象的类型,然后从类型(另一个 PyObject
结构)获取 tp_call
字段,即一个函数指针。如果 tp_call
不是 NULL
,它会通过 that 调用,并使用也传递给 PyObject_Call
.[=24= 的 args 和 kwargs 结构]
当 class 定义 __call__
方法时,会适当地设置 tp_call
字段。
这里有一篇文章详细解释了所有这些:Python internals: How callables work. It even lists and explains the entire PyObject_Call
function, which isn't very big. If you want to see that function in its native habitat, it's in Objects/abstract.c 在 CPython 存储库中。
这个 Whosebug 问答也很重要:What is a "callable" in Python?。
Python 的魔术方法 __call__
会在您尝试调用对象时被调用。 Cls()()
因此等于 Cls.__call__(Cls())
.
函数是 Python 中的第一个 class 对象,这意味着它们只是可调用对象(使用 __call__
)。但是,__call__
本身是一个函数,因此它也有 __call__
,它又有它自己的 __call__
,它又有它自己的 __call__
。
因此 Cls.__call__(Cls())
等于 Cls.__call__.__call__(Cls())
并再次等于 Cls.__call__.__call__.__call__(Cls())
等等。
这个无限循环是如何结束的? __call__
实际上是如何执行代码的?
没有真正的无限循环,因为在所有这些情况下 __call__
方法实际上并未被调用 ("called")。只有在对提供 __call__
方法的对象进行类似函数的调用时才会直接调用它。
正常 class 实例化 Cls(...)
和常规函数调用 f()
是直接处理的已知情况。通常没有 __call__()
的实际调用,因此可能会发生有限数量的 __call__
方法调用,即使在具有深度继承、metaclasses 等的复杂情况下也是如此.
因为对于概念无限循环的短路是否真的发生了一些争议,让我们看一下反汇编的字节码。考虑以下代码:
def f(x):
return x + 1
class Adder(object):
def something(self, x):
return x + 19
def __call__(self, x):
return x + 1
def lotsacalls(y):
u = f(1)
a = Adder()
z = u + a.something(y)
return a(z * 10)
抱歉,它有点复杂,因为我想展示几个短路实例——即普通 def
函数、__init__
调用、普通方法和 __call__
特殊方法。现在:
所以这里有一个时间范围,如果 Python 真的,真的 "walking the tree" 概念性 __call__
调用,它将引用 Function
(并且可能Method
classes,并调用它们的 __call__
方法)。它没有。它在所有情况下都使用简单的字节码 CALL_FUNCTION
,从而缩短了概念树的遍历。 逻辑上 你可以想象有一个 class Function
有一个 __call__
方法,当一个函数(即 Function
class) 被调用。但事实并非如此。编译器、字节码解释器和 C 语言基础的其他部分 实际上 遍历元 class 树。他们疯狂地短路。
我没有检查任何文档,但从我的测试看来 __call__
并不总是被调用:
def func1(*args, **kargs):
print "func1 called", args, kargs
def func2(*args, **kargs):
print "func2 called", args, kargs
func1.__call__ = func2
func1() # here is still called func1
class Cls:
def __init__(*args, **kargs):
print "init called", args, kargs
def __call__(*args, **kargs):
print "object called", args, kargs
obj = Cls() # here is actually called __init__
obj() # here is called __call__
这会打印
func1 called () {}
init called (<__main__.Cls instance at 0x0000000002A5ED88>,) {}
object called (<__main__.Cls instance at 0x0000000002A5ED88>,) {}
在幕后,Python 中的所有调用都使用相同的机制,并且几乎所有调用都到达 CPython 实现中的相同 C 函数。无论对象是具有 __call__
方法的 class 的实例、函数(本身是对象)还是内置对象,所有调用(优化的特殊情况除外)都会到达函数 PyObject_Call
。该 C 函数从对象的 PyObject
结构的 ob_type
字段获取对象的类型,然后从类型(另一个 PyObject
结构)获取 tp_call
字段,即一个函数指针。如果 tp_call
不是 NULL
,它会通过 that 调用,并使用也传递给 PyObject_Call
.[=24= 的 args 和 kwargs 结构]
当 class 定义 __call__
方法时,会适当地设置 tp_call
字段。
这里有一篇文章详细解释了所有这些:Python internals: How callables work. It even lists and explains the entire PyObject_Call
function, which isn't very big. If you want to see that function in its native habitat, it's in Objects/abstract.c 在 CPython 存储库中。
这个 Whosebug 问答也很重要:What is a "callable" in Python?。