呼叫協定 (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。
在 3.12 版的變更: 當一個類別的 __call__() 方法被重新賦值時,Py_TPFLAGS_HAVE_VECTORCALL 旗標現在會被移除。(這會在內部設定 tp_call,因此可能會使它與 vectorcall 函式有不同的行為。)在較早的 Python 版本中,vectorcall 應該只與 immutable 或靜態型別一起使用。
如果一個類別的 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)¶
- 為 穩定 ABI 的一部分 自 3.12 版本開始.
callable 是指被呼叫的物件。
args is a C array consisting of the positional arguments followed by the values of the keyword arguments. This can be NULL if there are no arguments.
nargsf is the number of positional arguments plus possibly the
PY_VECTORCALL_ARGUMENTS_OFFSETflag. To get the actual number of positional arguments from nargsf, usePyVectorcall_NARGS().kwnames is a tuple containing the names of the keyword arguments; in other words, the keys of the kwargs dict. These names must be strings (instances of
stror a subclass) and they must be unique. If there are no keyword arguments, then kwnames can instead be 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。這樣做會讓如 bound method(繫結方法)之類的可呼叫函式非常有效地繼續向前呼叫(這類函式包含一個在首位的 self 引數)。在 3.8 版被加入.
要呼叫一個實作了 vectorcall 的物件,請就像其他可呼叫物件一樣使用呼叫 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函式以便將來需要擴充。在 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)¶
- 為 穩定 ABI 的一部分 自 3.12 版本開始.
呼叫 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 |
|---|---|---|---|
|
tuple |
dict/ |
|
|
--- |
--- |
|
|
一個物件 |
--- |
|
|
tuple/ |
--- |
|
|
format |
--- |
|
物件 + |
format |
--- |
|
|
可變引數 |
--- |
|
物件 + 名稱 |
可變引數 |
--- |
|
物件 + 名稱 |
--- |
--- |
|
物件 + 名稱 |
一個物件 |
--- |
|
|
vectorcall |
vectorcall |
|
|
vectorcall |
dict/ |
|
引數 + 名稱 |
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 版的變更: name 和 format 的型別已從
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)¶
- 為 穩定 ABI 的一部分 自 3.12 版本開始.
呼叫一個可呼叫的 Python 物件 callable。附帶引數與
vectorcallfunc的相同。如果 callable 支援 vectorcall,則它會直接呼叫存放在 callable 中的 vectorcall 函式。成功時回傳結果,或在失敗時引發一個例外並回傳 NULL。
在 3.8 版被加入: 名為
_PyObject_Vectorcall在 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)¶
- 為 穩定 ABI 的一部分 自 3.12 版本開始.
使用 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)¶
- 為 穩定 ABI 的一部分.
判定物件 o 是否為可呼叫的。如果物件是可呼叫物件則回傳
1,其他情況回傳0。這個函式不會呼叫失敗。