调用协议

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

tp_call 协议

设置 tp_call 的类的实例都是可调用的。 槽位的签名为:

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

一个调用是用一个元组表示位置参数,用一个dict表示关键字参数,类似于Python代码中的``callable(args, **kwargs)``。args必须是非空的(如果没有参数,会使用一个空元组),但如果没有关键字参数,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

如果一个类的vectorcall比tp_call慢,就不应该实现vectorcall。例如,如果被调用者需要将参数转换为args 元组和kwargs dict,那么实现vectorcall就没有意义。

类可以通过启用 Py_TPFLAGS_HAVE_VECTORCALL 标志并将 tp_vectorcall_offset 设置为对象结构中的 vectorcallfunc 的 offset 来实现 vectorcall 协议。这是一个指向具有以下签名的函数的指针:

typedef PyObject (vectorcallfunc)(PyObject callable, PyObject **const args, size_t nargsf, PyObject kwnames)

  • callable 是指被调用的对象。

  • args 是一个C语言数组,由位置参数和后面的

    关键字参数的值。如果没有参数,这个值可以是 NULL

  • nargsf 是位置参数的数量加上可能的

    PY_VECTORCALL_ARGUMENTS_OFFSET 标志。 要从 nargsf 获得实际的位置参数数,请使用 PyVectorcall_NARGS()

  • kwnames 是一包含所有关键字名称的元组。

    换句话说,就是 kwargs 字典的键。 这些名字必须是字符串 (str 或其子类的实例),并且它们必须是唯一的。 如果没有关键字参数,那么 kwnames 可以用 NULL 代替。

PY_VECTORCALL_ARGUMENTS_OFFSET

如果在 vectorcall 的 nargsf 参数中设置了此标志,则允许被调用者临时更改 args[-1] 的值。换句话说, args 指向分配向量中的参数 1(不是 0 )。被调用方必须在返回之前还原 args[-1] 的值。

对于 PyObject_VectorcallMethod() ,这个标志的改变意味着``args[0]`` 可能改变了。

当调用方可以以几乎无代价的方式(无额外的内存申请),那么调用者被推荐适用: PY_VECTORCALL_ARGUMENTS_OFFSET。这样做将允许诸如绑定方法之类的可调用函数非常有效地进行向前调用(其中包括一个带前缀的 self 参数)。

要调用一个实现了 vectorcall 的对象,请使用某个 call 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 对于使用 tp_call 进行的调用会使用 Py_EnterRecursiveCall()Py_LeaveRecursiveCall()

为保证效率,这不适用于使用 vectorcall 的调用:被调用方在需要时应当使用 Py_EnterRecursiveCallPy_LeaveRecursiveCall

Vectorcall 支持 API

Py_ssize_t PyVectorcall_NARGS(size_t nargsf)

给定一个 vectorcall nargsf 实参,返回参数的实际数量。 目前等同于:

  1. (Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

然而,应使用 PyVectorcall_NARGS 函数以便将来扩展。

这个函数不是 limited API 的一部分。

3.8 新版功能.

vectorcallfunc PyVectorcall_Function(PyObject *op)

如果op不支持vectorcall协议(要么是因为类型不支持,要么是因为具体实例不支持),返回NULL。否则,返回存储在op中的vectorcall函数指针。这个函数从不触发异常。

这在检查 op 是否支持 vectorcall 时最有用处,可以通过检查 PyVectorcall_Function(op) != NULL 来实现。

这个函数不是 limited API 的一部分。

3.8 新版功能.

PyObject PyVectorcall_Call(PyObject **callable, PyObject tuple*, PyObject dict*)

调用可调对象vectorcallfunc,其位置参数和关键字参数分别以元组和dict形式给出。

这是一个专门函数,其目的是被放入 tp_call 槽位或是用于 tp_call 的实现。 它不会检查 Py_TPFLAGS_HAVE_VECTORCALL 旗标并且它不会回退到 tp_call

这个函数不是 limited API 的一部分。

3.8 新版功能.

调用对象的 API

有多个函数可被用来调用 Python 对象。 各个函数会将其参数转换为被调用对象所支持的惯例 – 可以是 tp_call 或 vectorcall。 为了尽可能少地进行转换,请选择一个适合你所拥有的数据格式的函数。

下表总结了可用的功能; 请参阅各个文档以了解详细信息。

函数

可调用对象(Callable)

args

kwargs

PyObject_Call()

PyObject

元组

dict/NULL

PyObject_CallNoArgs()

PyObject

—-

—-

PyObject_CallOneArg()

PyObject

1个对象

—-

PyObject_CallObject()

PyObject

元组/NULL

—-

PyObject_CallFunction()

PyObject

format

—-

PyObject_CallMethod()

对象 + char

format

—-

PyObject_CallFunctionObjArgs()

PyObject

可变参数

—-

PyObject_CallMethodObjArgs()

对象 + 名称

可变参数

—-

PyObject_CallMethodNoArgs()

对象 + 名称

—-

—-

PyObject_CallMethodOneArg()

对象 + 名称

1个对象

—-

PyObject_Vectorcall()

PyObject

vectorcall

vectorcall

PyObject_VectorcallDict()

PyObject *

vectorcall

dict/NULL

PyObject_VectorcallMethod()

参数 + 名称

vectorcall

vectorcall

PyObject PyObject_Call(PyObject **callable, PyObject args*, PyObject kwargs*)

Return value: New reference. Part of the Stable ABI.

调用一个可调用的 Python 对象 callable,附带由元组 args 所给出的参数,以及由字典 kwargs 所给出的关键字参数。

args 必须不为 NULL;如果不想要参数请使用一个空元组。 如果不想要关键字参数,则 kwargs 可以为 NULL

成功时返回结果,在失败时抛出一个异常并返回 NULL

这等价于 Python 表达式 callable(*args, **kwargs)

PyObject PyObject_CallNoArgs(PyObject **callable)

Part of the Stable ABI since version 3.10.

调用一个可调用的 Python 对象 callable 并不附带任何参数。 这是不带参数调用 Python 可调用对象的最有效方式。

成功时返回结果,在失败时抛出一个异常并返回 NULL

3.9 新版功能.

PyObject PyObject_CallOneArg(PyObject **callable, PyObject *arg)

调用一个可调用的 Python 对象 callable 并附带恰好 1 个位置参数 arg 而没有关键字参数。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这个函数不是 limited API 的一部分。

3.9 新版功能.

PyObject PyObject_CallObject(PyObject **callable, PyObject *args)

Return value: New reference. Part of the Stable ABI.

调用一个可调用的 Python 对象 callable,附带由元组 args 所给出的参数。 如果不想要传入参数,则 args 可以为 NULL

成功时返回结果,在失败时抛出一个异常并返回 NULL

这等价于 Python 表达式 callable(*args)

PyObject PyObject_CallFunction(PyObject **callable, const char *format, …)

Return value: New reference. Part of the Stable ABI.

调用一个可调用的 Python 对象 callable,附带可变数量的 C 参数。 这些 C 参数使用 Py_BuildValue() 风格的格式字符串来描述。 format 可以为 NULL,表示没有提供任何参数。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这等价于 Python 表达式 callable(*args)

请注意如果你只传入 PyObject* 参数,则 PyObject_CallFunctionObjArgs() 是更快速的选择。

在 3.4 版更改: 这个 format 类型已从 char * 更改。

PyObject PyObject_CallMethod(PyObject **obj, const char name, const* char format*, …)

Return value: New reference. Part of the Stable ABI.

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

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

成功时返回结果,在失败时抛出一个异常并返回 NULL

这和Python表达式``obj.name(arg1, arg2, …)``是一样的。

请注意如果你只传入 PyObject* 参数,则 PyObject_CallMethodObjArgs() 是更快速的选择。

在 3.4 版更改: The types of name and format were changed from char *.

PyObject PyObject_CallFunctionObjArgs(PyObject **callable, …)

Return value: New reference. Part of the Stable ABI.

调用一个可调用的 Python 对象 callable,附带可变数量的 PyObject 参数。 这些参数是以 NULL* 之后可变数量的形参的形式提供的。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这和Python表达式``callable(arg1, arg2, …)``是一样的。

PyObject PyObject_CallMethodObjArgs(PyObject **obj, PyObject *name, …)

Return value: New reference. Part of the Stable ABI.

调用 Python 对象 obj 中的一个方法,其中方法名称由 name 中的 Python 字符串对象给出。 它将附带可变数量的 PyObject 参数被调用。 这些参数是以 NULL* 之后可变数量的形参的形式提供的。

成功时返回结果,在失败时抛出一个异常并返回 NULL

PyObject PyObject_CallMethodNoArgs(PyObject **obj, PyObject *name)

调用 Python 对象 obj 中的一个方法并不附带任何参数,其中方法名称由 name 中的 Python 字符串对象给出。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这个函数不是 limited API 的一部分。

3.9 新版功能.

PyObject PyObject_CallMethodOneArg(PyObject **obj, PyObject name*, PyObject arg*)

调用 Python 对象 obj 中的一个方法并附带单个位置参数 arg,其中方法名称由 name 中的 Python 字符串对象给出。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这个函数不是 limited API 的一部分。

3.9 新版功能.

PyObject PyObject_Vectorcall(PyObject **callable, PyObject const* args, size_t nargsf, PyObject **kwnames)

调用一个可调用的 Python 对象 callable。 附带的参数与 vectorcallfunc 相同。 如果 callable 支持 vectorcall,则它会直接调用存放在 callable 中的 vectorcall 函数。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这个函数不是 limited API 的一部分。

3.9 新版功能.

PyObject PyObject_VectorcallDict(PyObject **callable, PyObject const* args, size_t nargsf, PyObject **kwdict)

调用 callable 并附带与在 vectorcall 协议中传入的完全相同的位置参数,但会加上以字典 kwdict 形式传入的关键字参数。 args 数组将只包含位置参数。

无论在内部使用哪种协议,都需要进行参数的转换。 因此,此函数应当仅在调用方已经拥有作为关键字参数的字典,但没有作为位置参数的元组时才被使用。

这个函数不是 limited API 的一部分。

3.9 新版功能.

PyObject PyObject_VectorcallMethod(PyObject **name, PyObject const* args, size_t nargsf, PyObject **kwnames)

使用 vectorcall 调用惯例来调用一个方法。 方法的名称以 Python 字符串 name 的形式给出。 调用方法的对象为 args[0],而 args 数组从 args[1] 开始的部分则代表调用的参数。 必须传入至少一个位置参数。 nargsf 为包括 args[0] 在内的位置参数的数量,如果 args[0] 的值可能被临时改变则要再加上 PY_VECTORCALL_ARGUMENTS_OFFSET。 关键字参数可以像在 PyObject_Vectorcall() 中一样被传入。

如果对象具有 Py_TPFLAGS_METHOD_DESCRIPTOR 特性,此函数将调用调用未绑定的方法对象并附带完整的 args vector 作为参数。

成功时返回结果,在失败时抛出一个异常并返回 NULL

这个函数不是 limited API 的一部分。

3.9 新版功能.

调用支持 API

int PyCallable_Check(PyObject *o)

Part of the Stable ABI.

确定对象 o 是可调对象。如果对象是可调对象则返回 1 ,其他情况返回 0 。这个函数不会调用失败。