呼叫協定 (Call Protocol)
************************

CPython 支援兩種不同的呼叫協定：*tp_call* 和 vectorcall（向量呼叫）。


*tp_call* 協定
==============

設定 "tp_call" 的類別之實例都是可呼叫的。該擴充槽 (slot) 的簽章為：

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

要達成一個呼叫會使用一個 tuple（元組）表示位置引數、一個 dict 表示關鍵
字引數，類似於 Python 程式碼中的 "callable(*args, **kwargs)"。*args*
必須不為 NULL（如果沒有引數，會使用一個空 tuple），但如果沒有關鍵字引
數，*kwargs* 可以是 *NULL*。

這個慣例不僅會被 *tp_call* 使用，"tp_new" 和 "tp_init" 也這樣傳遞引數
。

使用 "PyObject_Call()" 或其他呼叫 API 來呼叫一個物件。


Vectorcall 協定
===============

3.9 版新加入.

Vectorcall 協定是在 **PEP 590** 被引入的，它是使函式呼叫更加有效率的附
加協定。

經驗法則上，如果可呼叫物件有支援，CPython 於內部呼叫中會更傾向使用
vectorcall。然而，這並不是一個硬性規定。此外，有些第三方擴充套件會直接
使用 *tp_call*（而不是使用 "PyObject_Call()"）。因此，一個支援
vectorcall 的類別也必須實作 "tp_call"。此外，無論使用哪種協定，可呼叫
物件的行為都必須是相同的。要達成這個目的的推薦做法是將 "tp_call" 設定
為 "PyVectorcall_Call()"。這值得一再提醒：

警告:

  一個支援 vectorcall 的類別**必須**也實作具有相同語義的 "tp_call"。

如果一個類別的 vectorcall 比 *tp_call* 慢，就不應該實作 vectorcall。例
如，如果被呼叫者需要將引數轉換為 args tuple（引數元組）和 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 語言陣列 (array)，包含位置引數與後面
     關鍵字引數的值。如果沒有引數，這個值可以是 *NULL*。

* *nargsf* 是位置引數的數量加上可能會有的
     "PY_VECTORCALL_ARGUMENTS_OFFSET" 旗標。如果要從 *nargsf* 獲得實際
     的位置引數數量，請使用 "PyVectorcall_NARGS()"。

* *kwnames* 是一個包含所有關鍵字引數名稱的 tuple；
     換句話說，就是 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"。這樣做會讓如 bound
   method（繫結方法）之類的可呼叫函式非常有效地繼續向前呼叫（這類函式
   包含一個在首位的 *self* 引數）。

要呼叫一個實作了 vectorcall 的物件，請就像其他可呼叫物件一樣使用呼叫
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" 函式以便將來需要擴充。

   3.8 版新加入.

vectorcallfunc PyVectorcall_Function(PyObject *op)

   如果 *op* 不支援 vectorcall 協定（因為型別不支援或特定實例不支援）
   ，就回傳 *NULL*。否則，回傳儲存在 *op* 中的 vectorcall 函式指標。這
   個函式不會引發例外。

   這大多在檢查 *op* 是否支援 vectorcall 時能派上用場，可以透過檢查
   "PyVectorcall_Function(op) != NULL" 來達成。

   3.8 版新加入.

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

   呼叫 *callable* 的 "vectorcallfunc"，其位置引數和關鍵字引數分別以
   tuple 和 dict 格式給定。

   這是一個專門函式，其目的是被放入 "tp_call" 擴充槽或是用於 "tp_call"
   的實作。它不會檢查 "Py_TPFLAGS_HAVE_VECTORCALL" 旗標並且它不會退回
   (fall back) 使用 "tp_call"。

   3.8 版新加入.


物件呼叫 API
============

有多個函式可被用來呼叫 Python 物件。各個函式會將其引數轉換為被呼叫物件
所支援的慣用形式 – 可以是 *tp_call* 或 vectorcall。為了儘可能減少轉換
的進行，請選擇一個適合你所擁有資料格式的函式。

下表總結了可用的函式；請參閱各個說明文件以瞭解詳情。

+--------------------------------------------+--------------------+----------------------+-----------------+
| 函式                                       | callable           | args                 | kwargs          |
|============================================|====================|======================|=================|
| "PyObject_Call()"                          | "PyObject *"       | tuple                | dict/"NULL"     |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallNoArgs()"                    | "PyObject *"       | ---                  | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallOneArg()"                    | "PyObject *"       | 一個物件             | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallObject()"                    | "PyObject *"       | tuple/"NULL"         | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallFunction()"                  | "PyObject *"       | format               | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallMethod()"                    | 物件 + "char*"     | format               | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallFunctionObjArgs()"           | "PyObject *"       | 可變引數             | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallMethodObjArgs()"             | 物件 + 名稱        | 可變引數             | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallMethodNoArgs()"              | 物件 + 名稱        | ---                  | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_CallMethodOneArg()"              | 物件 + 名稱        | 一個物件             | ---             |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_Vectorcall()"                    | "PyObject *"       | vectorcall           | vectorcall      |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_VectorcallDict()"                | "PyObject *"       | vectorcall           | dict/"NULL"     |
+--------------------------------------------+--------------------+----------------------+-----------------+
| "PyObject_VectorcallMethod()"              | 引數 + 名稱        | vectorcall           | vectorcall      |
+--------------------------------------------+--------------------+----------------------+-----------------+

PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
    *返回值：新的引用。** Part of the Stable ABI.*

   呼叫一個可呼叫的 Python 物件 *callable*，附帶由 tuple *args* 所給定
   的引數及由字典 *kwargs* 所給定的關鍵字引數。

   *args* 必須不為 *NULL*；如果不需要引數，請使用一個空 tuple。如果不
   需要關鍵字引數，則 *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* 並附帶正好一個位置引數
   *arg* 而沒有關鍵字引數。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   3.9 版新加入.

PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)
    *返回值：新的引用。** Part of the Stable ABI.*

   呼叫一個可呼叫的 Python 物件 *callable*，附帶由 tuple *args* 所給定
   的引數。如果不需要傳入引數，則 *args* 可以為 *NULL*。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   這等價於 Python 運算式 "callable(*args)"。

PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)
    *返回值：新的引用。** Part of the Stable 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, ...)
    *返回值：新的引用。** Part of the Stable ABI.*

   呼叫 *obj* 物件中名為 *name* 的 method 並附帶數量可變的 C 引數。這
   些 C 引數由 "Py_BuildValue()" 格式字串來描述，並應當生成一個 tuple
   。

   格式可以為 *NULL*，表示沒有提供任何引數。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   這等價於 Python 運算式 "obj.name(arg1, arg2, ...)"。

   注意，如果你只傳入 "PyObject*" 引數，則
   "PyObject_CallMethodObjArgs()" 是另一個更快速的選擇。

   3.4 版更變: *name* 和 *format* 的型別已從 "char *" 更改。

PyObject *PyObject_CallFunctionObjArgs(PyObject *callable, ...)
    *返回值：新的引用。** Part of the Stable ABI.*

   呼叫一個可呼叫的 Python 物件 *callable*，附帶數量可變的 "PyObject*"
   引數。這些引數是以位置在 *NULL* 後面、數量可變的參數來提供。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   這等價於 Python 運算式 "callable(arg1, arg2, ...)"。

PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)
    *返回值：新的引用。** Part of the Stable ABI.*

   呼叫 Python 物件 *obj* 中的一個 method，其中 method 名稱由 *name*
   中的 Python 字串物件給定。被呼叫時會附帶數量可變的 "PyObject*" 引數
   。這些引數是以位置在 *NULL* 後面、且數量可變的參數來提供。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

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

   不附帶任何引數地呼叫 Python 物件 *obj* 中的一個 method，其中 method
   名稱由 *name* 中的 Python 字串物件給定。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   3.9 版新加入.

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

   附帶一個位置引數 *arg* 地呼叫 Python 物件 *obj* 中的一個 method，其
   中 method 名稱由 *name* 中的 Python 字串物件給定。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   3.9 版新加入.

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

   呼叫一個可呼叫的 Python 物件 *callable*。附帶引數與
   "vectorcallfunc" 的相同。如果 *callable* 支援 vectorcall，則它會直
   接呼叫存放在 *callable* 中的 vectorcall 函式。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   3.9 版新加入.

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

   附帶與在 vectorcall 協定中傳入的相同位置引數來呼叫 *callable*，但會
   加上以字典 *kwdict* 格式傳入的關鍵字引數。*args* 陣列將只包含位置引
   數。

   無論內部使用了哪一種協定，都會需要進行引數的轉換。因此，此函式應該
   只有在呼叫方已經擁有一個要作為關鍵字引數的字典、但沒有作為位置引數
   的 tuple 時才被使用。

   3.9 版新加入.

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

   使用 vectorcall 呼叫慣例來呼叫一個 method。method 的名稱以 Python
   字串 *name* 的格式給定。被呼叫 method 的物件為 *args[0]*，而 *args*
   陣列從 *args[1]* 開始的部分則代表呼叫的引數。必須傳入至少一個位置引
   數。*nargsf* 為包括 *args[0]* 在內的位置引數的數量，如果 "args[0]"
   的值可能被臨時改變則要再加上 "PY_VECTORCALL_ARGUMENTS_OFFSET"。關鍵
   字引數可以像在 "PyObject_Vectorcall()" 中一樣被傳入。

   如果物件具有 "Py_TPFLAGS_METHOD_DESCRIPTOR" 特性，這將以完整的
   *args* 向量作為引數來呼叫 unbound method（未繫結方法）物件。

   成功時回傳結果，或在失敗時引發一個例外並回傳 *NULL*。

   3.9 版新加入.


呼叫支援 API
============

int PyCallable_Check(PyObject *o)
    * Part of the Stable ABI.*

   判定物件 *o* 是否為可呼叫的。如果物件是可呼叫物件則回傳 "1"，其他情
   況回傳 "0"。這個函式不會呼叫失敗。
