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

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


*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 协议
===============

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 协议。这是一个指向具有以下签名的函数的指针:

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_EnterRecursiveCall* 和 *Py_LeaveRecursiveCall*。


Vectorcall 支持 API
-------------------

Py_ssize_t PyVectorcall_NARGS(size_t nargsf)

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

      (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。 为了尽可能少地进行转
换，请选择一个适合你所拥有的数据格式的函数。

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

+--------------------------------------------+--------------------+----------------------+-----------------+
| 函数                                       | 可调用对象（       | args                 | kwargs          |
|                                            | Callable）         |                      |                 |
|============================================|====================|======================|=================|
| "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.*

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

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

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

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

PyObject* PyObject_CallNoArgs(PyObject *callable)

   调用一个可调用的 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.*

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

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

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

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

   调用一个可调用的 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.*

   调用 *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.*

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

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

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

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

   调用 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)

   调用Call a method of the 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)

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