调用协议
********

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


*tp_call* 协议
==============

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

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

一个调用会使用一个元组表示位置参数，使用一个 dict 表示关键字参数，类似
于 Python 代码中的 "callable(*args, **kwargs)"。*args* 必须是非空的（
如果没有参数，会使用一个空元组），但如果没有关键字参数，*kwargs* 可以
是 *NULL*。

这个约定不仅被*tp_call*使用： "tp_new" 和 "tp_init" 也这样传递参数。

要调用一个对象，请使用 "PyObject_Call()" 或者其他的 调用 API。


Vectorcall 协议
===============

Added in version 3.9.

vectorcall 协议是在 **PEP 590** 被引入的，它是使调用函数更加有效的附加
协议。

作为经验法则，如果可调用对象支持 vectorcall，CPython 会更倾向于在内部
调用中使用它。然而，这并不是一个硬性规定。此外，一些第三方扩展直接使用
*tp_call* (而不是使用 "PyObject_Call()")。因此，一个支持 vectorcall 的
类也必须实现 "tp_call"。此外，无论使用哪种协议，可调用对象的行为都必须
是相同的。推荐的方法是将 "tp_call" 设置为 "PyVectorcall_Call()"。值得
再次强调的是：

警告:

  一个支持 Vectorcall 的类 **必须** 也实现具有相同语义的 "tp_call"。

在 3.12 版本发生变更: 现在 "Py_TPFLAGS_HAVE_VECTORCALL" 旗标在类的
"__call__()" 方法被重新赋值时将会从类中移除。 （这将仅在内部设置
"tp_call"，因此可能使其行为不同于 vectorcall 函数。）在更早的 Python
版本中，vectorcall 应当仅被用于 "不可变类型" 或静态类型。

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

类可以通过启用 "Py_TPFLAGS_HAVE_VECTORCALL" 旗标并将
"tp_vectorcall_offset" 设为对象结构体中 *vectorcallfunc* 出现位置偏移
量来实现 vectorcall 协议。这是一个指向具有以下签名的函数的指针：

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
    * 属于 稳定 ABI 自 3.12 版起.*

* *callable* 是指被调用的对象。

* *args* 是一个 C 语言数组，由位置参数和后面的
     关键字参数的值。如果没有参数，这个值可以是 *NULL* 。

* *nargsf* 是位置参数的数量加上可能的
     "PY_VECTORCALL_ARGUMENTS_OFFSET" 旗标。要从 *nargsf* 获得位置参数
     的实际数量，请使用 函数 "PyVectorcall_NARGS()"。

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

PY_VECTORCALL_ARGUMENTS_OFFSET
    * 属于 稳定 ABI 自 3.12 版起.*

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

   对于 "PyObject_VectorcallMethod()"，这个标志意味着 "args[0]" 可能会
   被改变。

   只要调用方能以低代价（不额外分配内存）这样做，就推荐使用
   "PY_VECTORCALL_ARGUMENTS_OFFSET"。 这样做将允许诸如绑定方法之类的可
   调用对象非常高效地执行前向调用（这种调用将包括一个加在开头的 *self*
   参数）。

   Added in version 3.8.

要调用一个实现了 vectorcall 的对象，请使用某个 call 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)
    * 属于 稳定 ABI 自 3.12 版起.*

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

      (Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

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

   Added in version 3.8.

vectorcallfunc PyVectorcall_Function(PyObject *op)

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

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

   Added in version 3.9.

PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)
    * 属于 稳定 ABI 自 3.12 版起.*

   调用 *callable* 的 "vectorcallfunc"，其位置参数和关键字参数分别以元
   组和 dict 形式给出。

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

   Added in version 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)
    *返回值：新的引用。** 属于 稳定 ABI.*

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

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

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

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

PyObject *PyObject_CallNoArgs(PyObject *callable)
    *返回值：新的引用。** 属于 稳定 ABI 自 3.10 版起.*

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

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

   Added in version 3.9.

PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)
    *返回值：新的引用。*

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

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

   Added in version 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()" 风格的格式字符串来描述。其中 format
   可以为 *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 版本发生变更: The types of *name* and *format* were changed
   from "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* 中的一个方法，其中方法名称由 *name* 中的
   Python 字符串对象给出。它将附带可变数量的 PyObject* 参数被调用。这
   些参数以可变数量的形参的形式提供并以 *NULL* 结尾。

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

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

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

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

   Added in version 3.9.

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

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

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

   Added in version 3.9.

PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
    * 属于 稳定 ABI 自 3.12 版起.*

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

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

   Added in version 3.8: 同 "_PyObject_Vectorcall"

   在 3.9 版本发生变更: 重命名为当前名称，不带开头的下划线。旧的暂定名
   称已设为 *soft deprecated*。

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

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

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

   Added in version 3.9.

PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)
    * 属于 稳定 ABI 自 3.12 版起.*

   使用 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*。

   Added in version 3.9.


调用支持 API
============

int PyCallable_Check(PyObject *o)
    * 属于 稳定 ABI.*

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