调用协议¶
CPython 支持两种不同的调用协议:tp_call 和 vectorcall。
tp_call 协议¶
设置了 tp_call
的类的实例是可调用的。该槽的签名为
PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);
调用使用元组作为位置参数,使用字典作为关键字参数,类似于 Python 代码中的 callable(*args, **kwargs)
。args 必须是非空(如果没有任何参数,则使用空元组),但如果没有任何关键字参数,kwargs 可以为 NULL。
这种约定不仅被 tp_call 使用:tp_new
和 tp_init
也以这种方式传递参数。
要调用一个对象,请使用 PyObject_Call()
或其他 调用 API。
Vectorcall 协议¶
在 3.9 版本中添加。
vectorcall 协议是在 PEP 590 中引入的,作为一种提高调用效率的额外协议。
作为经验法则,如果可调用对象支持 vectorcall,CPython 会优先使用它进行内部调用。但是,这不是一个硬性规则。此外,一些第三方扩展直接使用 tp_call(而不是使用 PyObject_Call()
)。因此,支持 vectorcall 的类也必须实现 tp_call
。此外,无论使用哪种协议,可调用对象都必须表现一致。实现这一点的推荐方法是将 tp_call
设置为 PyVectorcall_Call()
。需要再次强调
警告
支持 vectorcall 的类必须也实现 tp_call
,并具有相同的语义。
在 3.12 版本中更改: 当类的 __call__()
方法被重新分配时,Py_TPFLAGS_HAVE_VECTORCALL
标志现在将从该类中删除。(这在内部只设置 tp_call
,因此可能导致它的行为与 vectorcall 函数不同。)在早期的 Python 版本中,vectorcall 应该只与 immutable
或静态类型一起使用。
如果 vectorcall 比 tp_call 慢,则类不应该实现 vectorcall。例如,如果被调用者需要将参数转换为 args 元组和 kwargs 字典,那么实现 vectorcall 就没有意义。
类可以通过启用 Py_TPFLAGS_HAVE_VECTORCALL
标志并将 tp_vectorcall_offset
设置为对象结构中 vectorcallfunc 出现的偏移量来实现 vectorcall 协议。这是一个指向具有以下签名的函数的指针
-
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- 自 3.12 版本起,它是 稳定 ABI 的一部分。
callable 是被调用的对象。
- args 是一个 C 数组,包含位置参数,后面跟着
关键字参数的值。如果没有参数,它可以是 NULL。
- nargsf 是位置参数的数量,加上可能存在的
PY_VECTORCALL_ARGUMENTS_OFFSET
标志。要从 nargsf 获取实际的位置参数数量,请使用PyVectorcall_NARGS()
。
- kwnames 是一个元组,包含关键字参数的名称;
换句话说,kwargs 字典的键。这些名称必须是字符串(
str
或其子类的实例),并且必须是唯一的。如果没有关键字参数,则 kwnames 可以是 NULL。
-
PY_VECTORCALL_ARGUMENTS_OFFSET¶
- 自 3.12 版本起,它是 稳定 ABI 的一部分。
如果此标志在 vectorcall nargsf 参数中设置,则被调用者可以临时更改
args[-1]
。换句话说,args 指向分配的向量中的参数 1(而不是 0)。被调用者必须在返回之前恢复args[-1]
的值。对于
PyObject_VectorcallMethod()
,此标志意味着args[0]
可以被更改。只要它们可以廉价地做到(无需额外分配),调用者都鼓励使用
PY_VECTORCALL_ARGUMENTS_OFFSET
。这样做将允许诸如绑定方法之类的可调用对象非常有效地进行其后续调用(包括一个预先添加的 self 参数)。在 3.8 版本中添加。
要调用实现 vectorcall 的对象,请使用 调用 API 函数,就像使用任何其他可调用对象一样。 PyObject_Vectorcall()
通常是最有效的。
注意
在 CPython 3.8 中,vectorcall API 和相关函数以带下划线的名称临时提供:_PyObject_Vectorcall
、_Py_TPFLAGS_HAVE_VECTORCALL
、_PyObject_VectorcallMethod
、_PyVectorcall_Function
、_PyObject_CallOneArg
、_PyObject_CallMethodNoArgs
、_PyObject_CallMethodOneArg
。此外,PyObject_VectorcallDict
可作为 _PyObject_FastCallDict
使用。旧名称仍然定义为新名称(无下划线)的别名。
递归控制¶
使用 tp_call 时,被调用者无需担心 递归:CPython 使用 Py_EnterRecursiveCall()
和 Py_LeaveRecursiveCall()
来处理使用 tp_call 进行的调用。
为了提高效率,使用 vectorcall 进行的调用并非如此:如果需要,被调用者应使用 Py_EnterRecursiveCall 和 Py_LeaveRecursiveCall。
Vectorcall 支持 API¶
-
Py_ssize_t PyVectorcall_NARGS(size_t nargsf)¶
- 自 3.12 版本起,它是 稳定 ABI 的一部分。
给定 vectorcall nargsf 参数,返回实际参数数量。目前等效于
(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)
但是,应使用函数
PyVectorcall_NARGS
以便于将来扩展。在 3.8 版本中添加。
-
vectorcallfunc PyVectorcall_Function(PyObject *op)¶
如果 op 不支持 vectorcall 协议(无论是由于类型不支持还是由于特定实例不支持),则返回 NULL。否则,返回存储在 op 中的 vectorcall 函数指针。此函数从不引发异常。
这主要用于检查 op 是否支持 vectorcall,可以通过检查
PyVectorcall_Function(op) != NULL
来完成。在 3.9 版本中添加。
-
PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)¶
- 自 3.12 版本起,它是 稳定 ABI 的一部分。
使用分别在元组和字典中给出的位置参数和关键字参数调用 callable 的
vectorcallfunc
。这是一个专门的函数,旨在放在
tp_call
槽中或用于tp_call
的实现中。它不检查Py_TPFLAGS_HAVE_VECTORCALL
标志,也不回退到tp_call
。在 3.8 版本中添加。
对象调用 API¶
有多种函数可用于调用 Python 对象。每个函数都会将其参数转换为被调用对象支持的约定 - tp_call 或 vectorcall。为了尽可能减少转换,请选择最适合您可用数据格式的函数。
下表总结了可用的函数;有关详细信息,请参阅各个文档。
函数 |
可调用 |
参数 |
关键字参数 |
---|---|---|---|
|
元组 |
字典/ |
|
|
— |
— |
|
|
1 个对象 |
— |
|
|
元组/ |
— |
|
|
格式 |
— |
|
obj + |
格式 |
— |
|
|
可变参数 |
— |
|
obj + 名称 |
可变参数 |
— |
|
obj + 名称 |
— |
— |
|
obj + 名称 |
1 个对象 |
— |
|
|
vectorcall |
vectorcall |
|
|
vectorcall |
字典/ |
|
arg + 名称 |
vectorcall |
vectorcall |
-
PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)¶
- 返回值:新引用。 是 稳定 ABI 的一部分。
调用可调用 Python 对象 callable,参数由元组 args 给出,命名参数由字典 kwargs 给出。
args 不能为空;如果不需要参数,请使用空元组。如果不需要命名参数,kwargs 可以为 NULL。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
这等效于 Python 表达式:
callable(*args, **kwargs)
。
-
PyObject *PyObject_CallNoArgs(PyObject *callable)¶
- 返回值:新引用。 从版本 3.10 开始是 稳定 ABI 的一部分。
调用可调用 Python 对象 callable,不带任何参数。这是调用不带任何参数的可调用 Python 对象的最有效方式。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
在 3.9 版本中添加。
-
PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)¶
- 返回值:新引用。
调用可调用 Python 对象 callable,带有一个位置参数 arg,不带关键字参数。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
在 3.9 版本中添加。
-
PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)¶
- 返回值:新引用。 是 稳定 ABI 的一部分。
调用可调用 Python 对象 callable,参数由元组 args 给出。如果不需要参数,则 args 可以为 NULL。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
这等效于 Python 表达式:
callable(*args)
。
-
PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)¶
- 返回值:新引用。 是 稳定 ABI 的一部分。
使用可变数量的 C 语言参数调用可调用 Python 对象 callable。C 语言参数使用
Py_BuildValue()
样式的格式字符串进行描述。格式可以为 NULL,表示不提供任何参数。如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
这等效于 Python 表达式:
callable(*args)
。请注意,如果您只传递 PyObject* 参数,
PyObject_CallFunctionObjArgs()
是一个更快的替代方案。在版本 3.4 中更改: format 的类型已从
char *
更改。
-
PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)¶
- 返回值:新引用。 是 稳定 ABI 的一部分。
使用可变数量的 C 语言参数调用对象 obj 的名为 name 的方法。C 语言参数由
Py_BuildValue()
格式字符串描述,该字符串应生成一个元组。格式可以为 NULL,表示不提供任何参数。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
这等效于 Python 表达式:
obj.name(arg1, arg2, ...)
。请注意,如果您只传递 PyObject* 参数,
PyObject_CallMethodObjArgs()
是一个更快的替代方案。在版本 3.4 中更改: name 和 format 的类型已从
char *
更改。
-
PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...)¶
- 返回值:新引用。 是 稳定 ABI 的一部分。
使用可变数量的 PyObject* 参数调用可调用 Python 对象 callable。参数作为可变数量的参数提供,后面跟着 NULL。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
这等同于 Python 表达式:
callable(arg1, arg2, ...)
。
-
PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)¶
- 返回值:新引用。 是 稳定 ABI 的一部分。
调用 Python 对象 obj 的方法,方法名称作为 Python 字符串对象在 name 中给出。它被调用时带有多个 PyObject* 参数。参数作为多个参数提供,最后以 NULL 结尾。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
-
PyObject *PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)¶
调用 Python 对象 obj 的方法,不带参数,方法名称作为 Python 字符串对象在 name 中给出。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
在 3.9 版本中添加。
-
PyObject *PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)¶
调用 Python 对象 obj 的方法,带有一个位置参数 arg,方法名称作为 Python 字符串对象在 name 中给出。
如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
在 3.9 版本中添加。
-
PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- 自 3.12 版本起,它是 稳定 ABI 的一部分。
调用可调用 Python 对象 callable。参数与
vectorcallfunc
相同。如果 callable 支持 vectorcall,则直接调用存储在 callable 中的 vectorcall 函数。如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
在 3.9 版本中添加。
-
PyObject *PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)¶
使用与 vectorcall 协议中完全相同的顺序传递的位置参数调用 callable,但使用字典 kwdict 传递关键字参数。args 数组仅包含位置参数。
无论内部使用哪种协议,都需要进行参数转换。因此,只有在调用者已经准备好了一个用于关键字参数的字典,但没有用于位置参数的元组时,才应该使用此函数。
在 3.9 版本中添加。
-
PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- 自 3.12 版本起,它是 稳定 ABI 的一部分。
使用 vectorcall 调用约定调用方法。方法的名称作为 Python 字符串 name 给出。调用其方法的对象是 args[0],从 args[1] 开始的 args 数组表示调用的参数。必须至少有一个位置参数。nargsf 是包括 args[0] 在内的位置参数的数量,加上
PY_VECTORCALL_ARGUMENTS_OFFSET
(如果args[0]
的值可能暂时更改)。关键字参数可以像在PyObject_Vectorcall()
中一样传递。如果对象具有
Py_TPFLAGS_METHOD_DESCRIPTOR
特性,这将使用完整的 args 向量作为参数调用未绑定的方法对象。如果成功,则返回调用的结果,如果失败,则引发异常并返回 NULL。
在 3.9 版本中添加。