呼叫协议

CPython支持两种不同的调用协议: tp_call 还有矢量呼叫。

这个 tp_call 协议

类的实例 tp_call 是可以调用的。插槽的签名是:

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

调用时,位置参数使用元组,关键字参数使用dict,类似于 callable(*args, **kwargs) 在Python代码中。 args 必须为非空(如果没有参数,请使用空元组),但是 关键字参数 可能是 NULL 如果没有关键字参数。

本公约不仅由 tp_calltp_newtp_init 也可以这样传递参数。

要调用对象,请使用 PyObject_Call() 或其他 call API .

矢量调用协议

3.9 新版功能.

矢量调用协议在 PEP 590 作为提高通话效率的附加协议。

根据经验,如果可调用函数支持的话,CPython更喜欢内部调用的vectorcall。然而,这并不是一条硬性规定。另外,一些第三方扩展使用 tp_call 直接(而不是使用 PyObject_Call() ). 因此,支持vectorcall的类也必须实现 tp_call . 此外,无论使用哪种协议,可调用的行为都必须相同。建议通过设置 tp_callPyVectorcall_Call() . 这需要重复:

警告

支持向量调用的类 must 同时执行 tp_call 具有相同的语义。

如果一个类实现vectorcall的速度低于 tp_call . 例如,如果被调用方无论如何都需要将参数转换为args元组和kwargs dict,那么实现vectorcall就没有意义。

类可以通过启用 Py_TPFLAGS_HAVE_VECTORCALL 标志和设置 tp_vectorcall_offset 到对象结构内部的偏移,其中 矢量调用函数 出现。这是指向具有以下签名的函数的指针:

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
  • 可赎回的 是被调用的对象。

  • args 是一个C数组,由位置参数后跟

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

  • 纳尔斯夫 位置参数的数目加上

    PY_VECTORCALL_ARGUMENTS_OFFSET 旗帜。从中获取位置参数的实际数目 纳尔斯夫 使用 PyVectorcall_NARGS() .

  • KW名称 是包含关键字参数名称的元组;

    换句话说,kwargs dict的键。这些名称必须是字符串(例如 str 或者一个子类),它们必须是唯一的。如果没有关键字参数,则 KW名称 可以改为 NULL .

PY_VECTORCALL_ARGUMENTS_OFFSET

如果在矢量调用中设置了此标志 纳尔斯夫 参数,允许被调用方临时更改 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 ,被叫方不必担心 recursion :CPython使用 Py_EnterRecursiveCall()Py_LeaveRecursiveCall() 对于使用 tp_call .

为了提高效率,使用vectorcall完成的调用不是这样的:被调用方应该使用 Py_EnterRecursiveCallPy_LeaveRecursiveCall 如果需要的话。

矢量调用支持API

Py_ssize_t PyVectorcall_NARGS(size_t nargsf)

给了一个vectorcall 纳尔斯夫 参数,返回参数的实际数目。目前相当于:

(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

但是,功能 PyVectorcall_NARGS 应用于考虑将来的扩展。

此函数不是 limited API .

3.8 新版功能.

vectorcallfunc PyVectorcall_Function(PyObject *op)

如果 op 不支持vectorcall协议(因为类型不支持或因为特定实例不支持),返回 NULL . 否则,返回存储在 op . 此函数从不引发异常。

这对于检查是否 op 支持向量调用,可以通过检查 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 或矢量调用。为了尽可能地进行简单的转换,请选择最适合可用数据格式的转换。

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

功能

可赎回的

阿尔茨海默病

关键字参数

PyObject_Call()

PyObject *

元组

dict/

PyObject_CallNoArgs()

PyObject *

---

---

PyObject_CallOneArg()

PyObject *

1个对象

---

PyObject_CallObject()

PyObject *

元组/

---

PyObject_CallFunction()

PyObject *

格式

---

PyObject_CallMethod()

目标+ char*

格式

---

PyObject_CallFunctionObjArgs()

PyObject *

可变的

---

PyObject_CallMethodObjArgs()

对象+名称

可变的

---

PyObject_CallMethodNoArgs()

对象+名称

---

---

PyObject_CallMethodOneArg()

对象+名称

1个对象

---

PyObject_Vectorcall()

PyObject *

矢量呼叫

矢量呼叫

PyObject_VectorcallDict()

PyObject *

矢量呼叫

dict/

PyObject_VectorcallMethod()

arg+名称

矢量呼叫

矢量呼叫

PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
Return value: New reference.

调用可调用的python对象 可赎回的 ,由元组提供参数 args 和字典给出的命名参数 关键字参数 .

args 不得 NULL ;如果不需要参数,则使用空元组。如果不需要命名参数, 关键字参数 可以是 NULL .

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

这相当于python表达式: callable(*args, **kwargs) .

PyObject *PyObject_CallNoArgs(PyObject *callable)

调用可调用的python对象 可赎回的 没有任何参数。它是调用不带任何参数的可调用Python对象的最有效方法。

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

3.9 新版功能.

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

调用可调用的python对象 可赎回的 只有一个位置参数 arg 没有关键字参数。

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

此函数不是 limited API .

3.9 新版功能.

PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)
Return value: New reference.

调用可调用的python对象 可赎回的 ,由元组提供参数 args . 如果不需要参数,那么 args 可以是 NULL .

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

这相当于python表达式: callable(*args) .

PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)
Return value: New reference.

调用可调用的python对象 可赎回的 ,参数个数可变。C参数用 Py_BuildValue() 样式格式字符串。格式可以是 NULL ,指示未提供参数。

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

这相当于python表达式: callable(*args) .

注意,如果你只通过 PyObject* ARGS, PyObject_CallFunctionObjArgs() 是一个更快的选择。

在 3.4 版更改: 的类型 格式 已从更改 char * .

PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)
Return value: New reference.

调用名为的方法 name 对象的 obj 带有可变数量的C参数。c参数由a描述 Py_BuildValue() 应生成元组的格式字符串。

格式可以是 NULL ,指示未提供参数。

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

这相当于python表达式: obj.name(arg1, arg2, ...) .

注意,如果你只通过 PyObject* ARGS, PyObject_CallMethodObjArgs() 是一个更快的选择。

在 3.4 版更改: 类型 name格式 已从更改 char * .

PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...)
Return value: New reference.

调用可调用的python对象 可赎回的 ,变量为 PyObject* 参数。参数以参数的可变数目提供,后跟 NULL .

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

这相当于python表达式: callable(arg1, arg2, ...) .

PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
Return value: New reference.

调用python对象的方法 obj ,其中方法的名称作为python字符串对象提供 name . 它的调用变量为 PyObject* 参数。参数以参数的可变数目提供,后跟 NULL .

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

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

调用python对象的方法 obj 不带参数,其中方法的名称作为中的python字符串对象提供 name .

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

此函数不是 limited API .

3.9 新版功能.

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

调用python对象的方法 obj 只有一个位置参数 arg ,其中方法的名称作为python字符串对象提供 name .

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

此函数不是 limited API .

3.9 新版功能.

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

调用可调用的python对象 可赎回的 . 这些论点与 vectorcallfunc .如果 可赎回的 支架 vectorcall, 它直接调用存储在 可赎回的 .

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

此函数不是 limited API .

3.9 新版功能.

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

呼叫 可赎回的 传递的位置参数与 vectorcall 协议,但关键字参数作为字典传递 克威特 . 这个 args 数组只包含位置参数。

无论在内部使用哪种协议,都需要进行参数转换。因此,仅当调用方已准备好用于关键字参数的字典,而不是用于位置参数的元组时,才应使用此函数。

此函数不是 limited API .

3.9 新版功能.

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

使用vectorcall调用约定调用方法。方法的名称以Python字符串的形式给出 name . 调用方法的对象是 [参数[0]] 和 args 数组开始于 [参数[1]] 表示调用的参数。必须至少有一个位置参数。 纳尔斯夫 是位置参数的数目,包括 [参数[0]] 加上 PY_VECTORCALL_ARGUMENTS_OFFSET 如果值 args[0] 可临时更改。关键字参数的传递方式与 PyObject_Vectorcall() .

如果对象具有 Py_TPFLAGS_METHOD_DESCRIPTOR 功能,这将调用 args 矢量作为参数。

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

此函数不是 limited API .

3.9 新版功能.

调用支持API

int PyCallable_Check(PyObject *o)

确定对象是否 o 是可调用的。返回 1 如果对象是可调用的并且 0 否则。此函数总是成功的。