调用协议

CPython 支持两种不同的调用协议:`tp_call` 和 vectorcall。

`tp_call` 协议

设置了 tp_call 的类实例是可调用的。该槽的签名是

PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);

调用使用元组作为位置参数,使用字典作为关键字参数,类似于 Python 代码中的 callable(*args, **kwargs)。`args` 必须非 NULL(如果没有参数,则使用空元组),但如果没有任何关键字参数,`kwargs` 可以是 `NULL`。

此约定不仅由 `tp_call` 使用:tp_newtp_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 应该只与 不可变 或静态类型一起使用。

如果 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() 通常是最有效的。

递归控制

使用 `tp_call` 时,被调用者无需担心 递归:CPython 对使用 `tp_call` 进行的调用使用 Py_EnterRecursiveCall()Py_LeaveRecursiveCall()

为了效率,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。为了尽量减少转换,请选择最适合您可用数据格式的函数。

下表总结了可用函数;详情请参阅各自的文档。

函数

可调用

args

关键字参数

PyObject_Call()

PyObject *

元组

字典/NULL

PyObject_CallNoArgs()

PyObject *

PyObject_CallOneArg()

PyObject *

1 个对象

PyObject_CallObject()

PyObject *

元组/NULL

PyObject_CallFunction()

PyObject *

format

PyObject_CallMethod()

obj + char*

format

PyObject_CallFunctionObjArgs()

PyObject *

可变参数

PyObject_CallMethodObjArgs()

obj + 名称

可变参数

PyObject_CallMethodNoArgs()

obj + 名称

PyObject_CallMethodOneArg()

obj + 名称

1 个对象

PyObject_Vectorcall()

PyObject *

vectorcall

vectorcall

PyObject_VectorcallDict()

PyObject *

vectorcall

字典/NULL

PyObject_VectorcallMethod()

arg + 名称

vectorcall

vectorcall

PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
返回值: 新引用。 稳定ABI 的一部分。

调用可调用 Python 对象 `callable`,参数由元组 `args` 给出,命名参数由字典 `kwargs` 给出。

`args` 不得为 `NULL`;如果不需要参数,请使用空元组。如果不需要命名参数,`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`,带恰好 1 个位置参数 `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 的一部分。

调用可调用 Python 对象 `callable`,带可变数量的 C 参数。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 的一部分。

调用对象 `obj` 中名为 `name` 的方法,带可变数量的 C 参数。C 参数由 Py_BuildValue() 格式字符串描述,该字符串应生成一个元组。

格式可以为 `NULL`,表示不提供任何参数。

成功时返回调用的结果,失败时引发异常并返回 `NULL`。

这等同于 Python 表达式:obj.name(arg1, arg2, ...)

请注意,如果您只传递 PyObject* 参数,PyObject_CallMethodObjArgs() 是一个更快的替代方案。

3.4 版本中的变化: `name` 和 `format` 的类型从 char * 更改。

PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...)
返回值: 新引用。 稳定ABI 的一部分。

调用可调用 Python 对象 `callable`,带可变数量的 PyObject* 参数。参数作为可变数量的参数后跟 `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)

调用 `callable`,位置参数的传递方式与 vectorcall 协议完全相同,但关键字参数以字典 `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]` 在内的位置参数的数量,如果 `args[0]` 的值可能临时更改,则加上 PY_VECTORCALL_ARGUMENTS_OFFSET。关键字参数可以像 PyObject_Vectorcall() 中那样传递。

如果对象具有 Py_TPFLAGS_METHOD_DESCRIPTOR 功能,这将调用未绑定方法对象,并以完整的 `args` 向量作为参数。

成功时返回调用的结果,失败时引发异常并返回 `NULL`。

在 3.9 版本中新增。

调用支持 API

int PyCallable_Check(PyObject *o)
作为 稳定 ABI 的一部分。

确定对象 `o` 是否可调用。如果对象可调用则返回 1,否则返回 0。此函数始终成功。