調用協(xié)議?
CPython 支持兩種不同的調用協(xié)議:tp_call 和矢量調用。
tp_call 協(xié)議?
設置 tp_call
的類(lèi)的實(shí)例都是可調用的。 槽位的簽名為:
PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);
一個(gè)調用是用一個(gè)元組表示位置參數,用一個(gè)dict表示關(guān)鍵字參數,類(lèi)似于Python代碼中的``callable(args, **kwargs)``。*args*必須是非空的(如果沒(méi)有參數,會(huì )使用一個(gè)空元組),但如果沒(méi)有關(guān)鍵字參數,*kwargs*可以是*NULL。
這個(gè)約定不僅被*tp_call*使用: tp_new
和 tp_init
也這樣傳遞參數。
To call an object, use PyObject_Call()
or another
call API.
Vectorcall 協(xié)議?
3.9 新版功能.
vectorcall 協(xié)議是在 PEP 590 被引入的,它是使調用函數更加有效的附加協(xié)議。
作為經(jīng)驗法則,如果可調用程序支持 vectorcall,CPython 會(huì )更傾向于內聯(lián)調用。 然而,這并不是一個(gè)硬性規定。 此外,一些第三方擴展直接使用 tp_call (而不是使用 PyObject_Call()
)。 因此,一個(gè)支持 vectorcall 的類(lèi)也必須實(shí)現 tp_call
。 此外,無(wú)論使用哪種協(xié)議,可調對象的行為都必須是相同的。 推薦的方法是將 tp_call
設置為 PyVectorcall_Call()
。值得一提的是:
警告
一個(gè)支持 Vectorcall 的類(lèi) 必須 也實(shí)現具有相同語(yǔ)義的 tp_call
。
如果一個(gè)類(lèi)的vectorcall比*tp_call*慢,就不應該實(shí)現vectorcall。例如,如果被調用者需要將參數轉換為args 元組和kwargs dict,那么實(shí)現vectorcall就沒(méi)有意義。
類(lèi)可以通過(guò)啟用 Py_TPFLAGS_HAVE_VECTORCALL
標志并將 tp_vectorcall_offset
設置為對象結構中的 vectorcallfunc 的 offset 來(lái)實(shí)現 vectorcall 協(xié)議。這是一個(gè)指向具有以下簽名的函數的指針:
-
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)?
callable 是指被調用的對象。
- args 是一個(gè)C語(yǔ)言數組,由位置參數和后面的
關(guān)鍵字參數的值。如果沒(méi)有參數,這個(gè)值可以是 NULL 。
- nargsf 是位置參數的數量加上可能的
PY_VECTORCALL_ARGUMENTS_OFFSET
標志。 要從 nargsf 獲得實(shí)際的位置參數數,請使用PyVectorcall_NARGS()
。
- kwnames 是一包含所有關(guān)鍵字名稱(chēng)的元組。
換句話(huà)說(shuō),就是 kwargs 字典的鍵。 這些名字必須是字符串 (
str
或其子類(lèi)的實(shí)例),并且它們必須是唯一的。 如果沒(méi)有關(guān)鍵字參數,那么 kwnames 可以用 NULL 代替。
-
PY_VECTORCALL_ARGUMENTS_OFFSET?
如果在 vectorcall 的 nargsf 參數中設置了此標志,則允許被調用者臨時(shí)更改
args[-1]
的值。換句話(huà)說(shuō), args 指向分配向量中的參數 1(不是 0 )。被調用方必須在返回之前還原args[-1]
的值。對于
PyObject_VectorcallMethod()
,這個(gè)標志的改變意味著(zhù)``args[0]`` 可能改變了。當調用方可以以幾乎無(wú)代價(jià)的方式(無(wú)額外的內存申請),那么調用者被推薦適用:
PY_VECTORCALL_ARGUMENTS_OFFSET
。這樣做將允許諸如綁定方法之類(lèi)的可調用函數非常有效地進(jìn)行向前調用(其中包括一個(gè)帶前綴的 self 參數)。
要調用一個(gè)實(shí)現了 vectorcall 的對象,請使用某個(gè) call API 函數,就像其他可調對象一樣。 PyObject_Vectorcall()
通常是最有效的。
備注
在 CPython 3.8 中,vectorcall API 和相關(guān)的函數暫定以帶開(kāi)頭下劃線(xiàn)的名稱(chēng)提供: _PyObject_Vectorcall
, _Py_TPFLAGS_HAVE_VECTORCALL
, _PyObject_VectorcallMethod
, _PyVectorcall_Function
, _PyObject_CallOneArg
, _PyObject_CallMethodNoArgs
, _PyObject_CallMethodOneArg
。 此外, PyObject_VectorcallDict
以 _PyObject_FastCallDict
的名稱(chēng)提供。 舊名稱(chēng)仍然被定義為不帶下劃線(xiàn)的新名稱(chēng)的別名。
遞歸控制?
在使用 tp_call 時(shí),被調用者不必擔心 遞歸: CPython 對于使用 tp_call 進(jìn)行的調用會(huì )使用 Py_EnterRecursiveCall()
和 Py_LeaveRecursiveCall()
。
為保證效率,這不適用于使用 vectorcall 的調用:被調用方在需要時(shí)應當使用 Py_EnterRecursiveCall 和 Py_LeaveRecursiveCall。
Vectorcall 支持 API?
-
Py_ssize_t PyVectorcall_NARGS(size_t nargsf)?
給定一個(gè) vectorcall nargsf 實(shí)參,返回參數的實(shí)際數量。 目前等同于:
(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)
然而,應使用
PyVectorcall_NARGS
函數以便將來(lái)擴展。這個(gè)函數不是 limited API 的一部分。
3.8 新版功能.
-
vectorcallfunc PyVectorcall_Function(PyObject *op)?
如果*op*不支持vectorcall協(xié)議(要么是因為類(lèi)型不支持,要么是因為具體實(shí)例不支持),返回*NULL*。否則,返回存儲在*op*中的vectorcall函數指針。這個(gè)函數從不觸發(fā)異常。
這在檢查 op 是否支持 vectorcall 時(shí)最有用處,可以通過(guò)檢查
PyVectorcall_Function(op) != NULL
來(lái)實(shí)現。這個(gè)函數不是 limited API 的一部分。
3.8 新版功能.
-
PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)?
調用*可調對象*的
vectorcallfunc
,其位置參數和關(guān)鍵字參數分別以元組和dict形式給出。這是一個(gè)專(zhuān)門(mén)函數,其目的是被放入
tp_call
槽位或是用于tp_call
的實(shí)現。 它不會(huì )檢查Py_TPFLAGS_HAVE_VECTORCALL
旗標并且它不會(huì )回退到tp_call
。這個(gè)函數不是 limited API 的一部分。
3.8 新版功能.
調用對象的 API?
Various functions are available for calling a Python object. Each converts its arguments to a convention supported by the called object – either tp_call or vectorcall. In order to do as little conversion as possible, pick one that best fits the format of data you have available.
下表總結了可用的功能; 請參閱各個(gè)文檔以了解詳細信息。
函數 |
可調用對象(Callable) |
args |
kwargs |
---|---|---|---|
|
元組 |
dict/ |
|
|
--- |
--- |
|
|
1個(gè)對象 |
--- |
|
|
元組/ |
--- |
|
|
format |
--- |
|
對象 + |
format |
--- |
|
|
可變參數 |
--- |
|
對象 + 名稱(chēng) |
可變參數 |
--- |
|
對象 + 名稱(chēng) |
--- |
--- |
|
對象 + 名稱(chēng) |
1個(gè)對象 |
--- |
|
|
vectorcall |
vectorcall |
|
|
vectorcall |
dict/ |
|
參數 + 名稱(chēng) |
vectorcall |
vectorcall |
-
PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)?
- Return value: New reference. Part of the Stable ABI.
調用一個(gè)可調用的 Python 對象 callable,附帶由元組 args 所給出的參數,以及由字典 kwargs 所給出的關(guān)鍵字參數。
args 必須不為 NULL;如果不想要參數請使用一個(gè)空元組。 如果不想要關(guān)鍵字參數,則 kwargs 可以為 NULL。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這等價(jià)于 Python 表達式
callable(*args, **kwargs)
。
-
PyObject *PyObject_CallNoArgs(PyObject *callable)?
- Part of the Stable ABI since version 3.10.
調用一個(gè)可調用的 Python 對象 callable 并不附帶任何參數。 這是不帶參數調用 Python 可調用對象的最有效方式。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
3.9 新版功能.
-
PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)?
調用一個(gè)可調用的 Python 對象 callable 并附帶恰好 1 個(gè)位置參數 arg 而沒(méi)有關(guān)鍵字參數。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這個(gè)函數不是 limited API 的一部分。
3.9 新版功能.
-
PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)?
- Return value: New reference. Part of the Stable ABI.
調用一個(gè)可調用的 Python 對象 callable,附帶由元組 args 所給出的參數。 如果不想要傳入參數,則 args 可以為 NULL。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這等價(jià)于 Python 表達式
callable(*args)
。
-
PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)?
- Return value: New reference. Part of the Stable ABI.
調用一個(gè)可調用的 Python 對象 callable,附帶可變數量的 C 參數。 這些 C 參數使用
Py_BuildValue()
風(fēng)格的格式字符串來(lái)描述。 format 可以為 NULL,表示沒(méi)有提供任何參數。成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這等價(jià)于 Python 表達式
callable(*args)
。請注意如果你只傳入 PyObject* 參數,則
PyObject_CallFunctionObjArgs()
是更快速的選擇。在 3.4 版更改: 這個(gè) format 類(lèi)型已從
char *
更改。
-
PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)?
- Return value: New reference. Part of the Stable ABI.
調用 obj 對象中名為 name 的方法并附帶可變數量的 C 參數。 這些 C 參數由
Py_BuildValue()
格式字符串來(lái)描述并應當生成一個(gè)元組。格式可以為 NULL ,表示未提供任何參數。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 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. Part of the Stable ABI.
調用一個(gè)可調用的 Python 對象 callable,附帶可變數量的 PyObject* 參數。 這些參數是以 NULL 之后可變數量的形參的形式提供的。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這和Python表達式``callable(arg1, arg2, ...)``是一樣的。
-
PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)?
- Return value: New reference. Part of the Stable ABI.
調用 Python 對象 obj 中的一個(gè)方法,其中方法名稱(chēng)由 name 中的 Python 字符串對象給出。 它將附帶可變數量的 PyObject* 參數被調用。 這些參數是以 NULL 之后可變數量的形參的形式提供的。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
-
PyObject *PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)?
調用 Python 對象 obj 中的一個(gè)方法并不附帶任何參數,其中方法名稱(chēng)由 name 中的 Python 字符串對象給出。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這個(gè)函數不是 limited API 的一部分。
3.9 新版功能.
-
PyObject *PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)?
調用 Python 對象 obj 中的一個(gè)方法并附帶單個(gè)位置參數 arg,其中方法名稱(chēng)由 name 中的 Python 字符串對象給出。
成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這個(gè)函數不是 limited API 的一部分。
3.9 新版功能.
-
PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)?
調用一個(gè)可調用的 Python 對象 callable。 附帶的參數與
vectorcallfunc
相同。 如果 callable 支持 vectorcall,則它會(huì )直接調用存放在 callable 中的 vectorcall 函數。成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這個(gè)函數不是 limited API 的一部分。
3.9 新版功能.
-
PyObject *PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)?
調用 callable 并附帶與在 vectorcall 協(xié)議中傳入的完全相同的位置參數,但會(huì )加上以字典 kwdict 形式傳入的關(guān)鍵字參數。 args 數組將只包含位置參數。
無(wú)論在內部使用哪種協(xié)議,都需要進(jìn)行參數的轉換。 因此,此函數應當僅在調用方已經(jīng)擁有作為關(guān)鍵字參數的字典,但沒(méi)有作為位置參數的元組時(shí)才被使用。
這個(gè)函數不是 limited API 的一部分。
3.9 新版功能.
-
PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)?
使用 vectorcall 調用慣例來(lái)調用一個(gè)方法。 方法的名稱(chēng)以 Python 字符串 name 的形式給出。 調用方法的對象為 args[0],而 args 數組從 args[1] 開(kāi)始的部分則代表調用的參數。 必須傳入至少一個(gè)位置參數。 nargsf 為包括 args[0] 在內的位置參數的數量,如果
args[0]
的值可能被臨時(shí)改變則要再加上PY_VECTORCALL_ARGUMENTS_OFFSET
。 關(guān)鍵字參數可以像在PyObject_Vectorcall()
中一樣被傳入。如果對象具有
Py_TPFLAGS_METHOD_DESCRIPTOR
特性,此函數將調用調用未綁定的方法對象并附帶完整的 args vector 作為參數。成功時(shí)返回結果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。
這個(gè)函數不是 limited API 的一部分。
3.9 新版功能.
調用支持 API?
-
int PyCallable_Check(PyObject *o)?
- Part of the Stable ABI.
確定對象 o 是可調對象。如果對象是可調對象則返回
1
,其他情況返回0
。這個(gè)函數不會(huì )調用失敗。