呼叫協定 (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_newtp_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 就沒有意義。

Classes can implement the vectorcall protocol by enabling the Py_TPFLAGS_HAVE_VECTORCALL flag and setting tp_vectorcall_offset to the offset inside the object structure where a vectorcallfunc appears. This is a pointer to a function with the following signature:

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
  • callable 是指被呼叫的物件。

  • args 是一個 C 語言陣列 (array),包含位置引數與後面

    關鍵字引數的值。如果沒有引數,這個值可以是 NULL

  • nargsf 是位置引數的數量加上可能會有的

    PY_VECTORCALL_ARGUMENTS_OFFSET flag. To get the actual number of positional arguments from nargsf, use 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] 被改變。

Whenever they can do so cheaply (without additional allocation), callers are encouraged to use PY_VECTORCALL_ARGUMENTS_OFFSET. Doing so will allow callables such as bound methods to make their onward calls (which include a prepended self argument) very efficiently.

在 3.8 版新加入.

要呼叫一個實作了 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_EnterRecursiveCallPy_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.9 版新加入.

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

呼叫 callablevectorcallfunc,其位置引數和關鍵字引數分別以 tuple 和 dict 格式給定。

This is a specialized function, intended to be put in the tp_call slot or be used in an implementation of tp_call. It does not check the Py_TPFLAGS_HAVE_VECTORCALL flag and it does not fall back to 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)
回傳值:新的參照。 属于 稳定 ABI.

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

args 必須不為 NULL;如果不需要引數,請使用一個空 tuple。如果不需要關鍵字引數,則 kwargs 可以為 NULL

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

這等價於 Python 運算式 callable(*args, **kwargs)

PyObject *PyObject_CallNoArgs(PyObject *callable)
回傳值:新的參照。 属于 稳定 ABI 自 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)
回傳值:新的參照。 属于 稳定 ABI.

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

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

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

PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)
回傳值:新的參照。 属于 稳定 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, ...)
回傳值:新的參照。 属于 稳定 ABI.

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

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

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

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

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

在 3.4 版的變更: nameformat 的型別已從 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 中的一個 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)

Call a method using the vectorcall calling convention. The name of the method is given as a Python string name. The object whose method is called is args[0], and the args array starting at args[1] represents the arguments of the call. There must be at least one positional argument. nargsf is the number of positional arguments including args[0], plus PY_VECTORCALL_ARGUMENTS_OFFSET if the value of args[0] may temporarily be changed. Keyword arguments can be passed just like in PyObject_Vectorcall().

If the object has the Py_TPFLAGS_METHOD_DESCRIPTOR feature, this will call the unbound method object with the full args vector as arguments.

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

在 3.9 版新加入.

呼叫支援 API

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

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