呼叫協定 (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 版的變更:ThePy_TPFLAGS_HAVE_VECTORCALL
flag is now removed from a classwhen the class's__call__()
method is reassigned.(This internally setstp_call
only, and thusmay make it behave differently than the vectorcall function.)In earlier Python versions, vectorcall should only be used withimmutable
or static types.
如果一個類別的 vectorcall 比tp_call 慢,就不應該實作 vectorcall。例如,如果被呼叫者需要將引數轉換為 args tuple(引數元組)和 kwargs dict(關鍵字引數字典),那麼實作 vectorcall 就沒有意義。
類別可以透過啟用Py_TPFLAGS_HAVE_VECTORCALL
旗標並將tp_vectorcall_offset
設定為物件結構中有出現vectorcallfunc 的 offset 來實作 vectorcall 協定。這是一個指向具有以下簽章之函式的指標:
- typedefPyObject*(*vectorcallfunc)(PyObject*callable,PyObject*const*args,size_tnargsf,PyObject*kwnames)¶
- 為穩定 ABI 的一部分 自 3.12 版本開始.
callable 是指被呼叫的物件。
- args 是一個 C 語言陣列 (array),包含位置引數與後面
關鍵字引數的值。如果沒有引數,這個值可以是NULL。
- nargsf 是位置引數的數量加上可能會有的
PY_VECTORCALL_ARGUMENTS_OFFSET
旗標。如果要從nargsf 獲得實際的位置引數數量,請使用PyVectorcall_NARGS()
。
- kwnames 是一個包含所有關鍵字引數名稱的 tuple;
換句話說,就是 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
。這樣做會讓如 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_tPyVectorcall_NARGS(size_tnargsf)¶
- 為穩定 ABI 的一部分 自 3.12 版本開始.
給定一個 vectorcallnargsf 引數,回傳引數的實際數量。目前等同於:
(Py_ssize_t)(nargsf&~PY_VECTORCALL_ARGUMENTS_OFFSET)
然而,應使用
PyVectorcall_NARGS
函式以便將來需要擴充。在 3.8 版被加入.
- vectorcallfuncPyVectorcall_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,附帶由 tupleargs 所給定的引數及由字典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,附帶由 tupleargs 所給定的引數。如果不需要傳入引數,則args 可以為NULL。
成功時回傳結果,或在失敗時引發一個例外並回傳NULL。
這等價於 Python 運算式
callable(*args)
。
- PyObject*PyObject_CallFunction(PyObject*callable,constchar*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,constchar*name,constchar*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_tnargsf,PyObject*kwnames)¶
- 為穩定 ABI 的一部分 自 3.12 版本開始.
呼叫一個可呼叫的 Python 物件callable。附帶引數與
vectorcallfunc
的相同。如果callable 支援vectorcall,則它會直接呼叫存放在callable 中的 vectorcall 函式。成功時回傳結果,或在失敗時引發一個例外並回傳NULL。
在 3.9 版被加入.
- PyObject*PyObject_VectorcallDict(PyObject*callable,PyObject*const*args,size_tnargsf,PyObject*kwdict)¶
附帶與在vectorcall 協定中傳入的相同位置引數來呼叫callable,但會加上以字典kwdict 格式傳入的關鍵字引數。args 陣列將只包含位置引數。
無論內部使用了哪一種協定,都會需要進行引數的轉換。因此,此函式應該只有在呼叫方已經擁有一個要作為關鍵字引數的字典、但沒有作為位置引數的 tuple 時才被使用。
在 3.9 版被加入.
- PyObject*PyObject_VectorcallMethod(PyObject*name,PyObject*const*args,size_tnargsf,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¶
- intPyCallable_Check(PyObject*o)¶
- 為穩定 ABI 的一部分.
判定物件o 是否為可呼叫的。如果物件是可呼叫物件則回傳
1
,其他情況回傳0
。這個函式不會呼叫失敗。