調(diào)用協(xié)議?

CPython 支持兩種不同的調(diào)用協(xié)議:tp_call 和矢量調(diào)用。

tp_call 協(xié)議?

設(shè)置 tp_call 的類的實(shí)例都是可調(diào)用的。 槽位的簽名為:

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

一個(gè)調(diào)用是用一個(gè)元組表示位置參數(shù),用一個(gè)dict表示關(guān)鍵字參數(shù),類似于Python代碼中的``callable(args, **kwargs)``。*args*必須是非空的(如果沒有參數(shù),會使用一個(gè)空元組),但如果沒有關(guān)鍵字參數(shù),*kwargs*可以是*NULL。

這個(gè)約定不僅被*tp_call*使用: tp_newtp_init 也這樣傳遞參數(shù)。

To call an object, use PyObject_Call() or another call API.

Vectorcall 協(xié)議?

3.9 新版功能.

vectorcall 協(xié)議是在 PEP 590 被引入的,它是使調(diào)用函數(shù)更加有效的附加協(xié)議。

作為經(jīng)驗(yàn)法則,如果可調(diào)用程序支持 vectorcall,CPython 會更傾向于內(nèi)聯(lián)調(diào)用。 然而,這并不是一個(gè)硬性規(guī)定。 此外,一些第三方擴(kuò)展直接使用 tp_call (而不是使用 PyObject_Call())。 因此,一個(gè)支持 vectorcall 的類也必須實(shí)現(xiàn) tp_call。 此外,無論使用哪種協(xié)議,可調(diào)對象的行為都必須是相同的。 推薦的方法是將 tp_call 設(shè)置為 PyVectorcall_Call()。值得一提的是:

警告

一個(gè)支持 Vectorcall 的類 必須 也實(shí)現(xiàn)具有相同語義的 tp_call。

如果一個(gè)類的vectorcall比*tp_call*慢,就不應(yīng)該實(shí)現(xiàn)vectorcall。例如,如果被調(diào)用者需要將參數(shù)轉(zhuǎn)換為args 元組和kwargs dict,那么實(shí)現(xiàn)vectorcall就沒有意義。

類可以通過啟用 Py_TPFLAGS_HAVE_VECTORCALL 標(biāo)志并將 tp_vectorcall_offset 設(shè)置為對象結(jié)構(gòu)中的 vectorcallfunc 的 offset 來實(shí)現(xiàn) vectorcall 協(xié)議。這是一個(gè)指向具有以下簽名的函數(shù)的指針:

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)?
  • callable 是指被調(diào)用的對象。

  • args 是一個(gè)C語言數(shù)組,由位置參數(shù)和后面的

    關(guān)鍵字參數(shù)的值。如果沒有參數(shù),這個(gè)值可以是 NULL 。

  • nargsf 是位置參數(shù)的數(shù)量加上可能的

    PY_VECTORCALL_ARGUMENTS_OFFSET 標(biāo)志。 要從 nargsf 獲得實(shí)際的位置參數(shù)數(shù),請使用 PyVectorcall_NARGS()。

  • kwnames 是一包含所有關(guān)鍵字名稱的元組。

    換句話說,就是 kwargs 字典的鍵。 這些名字必須是字符串 (str 或其子類的實(shí)例),并且它們必須是唯一的。 如果沒有關(guān)鍵字參數(shù),那么 kwnames 可以用 NULL 代替。

PY_VECTORCALL_ARGUMENTS_OFFSET?

如果在 vectorcall 的 nargsf 參數(shù)中設(shè)置了此標(biāo)志,則允許被調(diào)用者臨時(shí)更改 args[-1] 的值。換句話說, args 指向分配向量中的參數(shù) 1(不是 0 )。被調(diào)用方必須在返回之前還原 args[-1] 的值。

對于 PyObject_VectorcallMethod() ,這個(gè)標(biāo)志的改變意味著``args[0]`` 可能改變了。

當(dāng)調(diào)用方可以以幾乎無代價(jià)的方式(無額外的內(nèi)存申請),那么調(diào)用者被推薦適用: PY_VECTORCALL_ARGUMENTS_OFFSET。這樣做將允許諸如綁定方法之類的可調(diào)用函數(shù)非常有效地進(jìn)行向前調(diào)用(其中包括一個(gè)帶前綴的 self 參數(shù))。

要調(diào)用一個(gè)實(shí)現(xiàn)了 vectorcall 的對象,請使用某個(gè) call API 函數(shù),就像其他可調(diào)對象一樣。 PyObject_Vectorcall() 通常是最有效的。

備注

在 CPython 3.8 中,vectorcall API 和相關(guān)的函數(shù)暫定以帶開頭下劃線的名稱提供: _PyObject_Vectorcall, _Py_TPFLAGS_HAVE_VECTORCALL, _PyObject_VectorcallMethod, _PyVectorcall_Function, _PyObject_CallOneArg, _PyObject_CallMethodNoArgs, _PyObject_CallMethodOneArg。 此外, PyObject_VectorcallDict_PyObject_FastCallDict 的名稱提供。 舊名稱仍然被定義為不帶下劃線的新名稱的別名。

遞歸控制?

在使用 tp_call 時(shí),被調(diào)用者不必?fù)?dān)心 遞歸: CPython 對于使用 tp_call 進(jìn)行的調(diào)用會使用 Py_EnterRecursiveCall()Py_LeaveRecursiveCall()。

為保證效率,這不適用于使用 vectorcall 的調(diào)用:被調(diào)用方在需要時(shí)應(yīng)當(dāng)使用 Py_EnterRecursiveCallPy_LeaveRecursiveCall。

Vectorcall 支持 API?

Py_ssize_t PyVectorcall_NARGS(size_t nargsf)?

給定一個(gè) vectorcall nargsf 實(shí)參,返回參數(shù)的實(shí)際數(shù)量。 目前等同于:

(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

然而,應(yīng)使用 PyVectorcall_NARGS 函數(shù)以便將來擴(kuò)展。

這個(gè)函數(shù)不是 limited API 的一部分。

3.8 新版功能.

vectorcallfunc PyVectorcall_Function(PyObject *op)?

如果*op*不支持vectorcall協(xié)議(要么是因?yàn)轭愋筒恢С?,要么是因?yàn)榫唧w實(shí)例不支持),返回*NULL*。否則,返回存儲在*op*中的vectorcall函數(shù)指針。這個(gè)函數(shù)從不觸發(fā)異常。

這在檢查 op 是否支持 vectorcall 時(shí)最有用處,可以通過檢查 PyVectorcall_Function(op) != NULL 來實(shí)現(xiàn)。

這個(gè)函數(shù)不是 limited API 的一部分。

3.8 新版功能.

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

調(diào)用*可調(diào)對象*的 vectorcallfunc,其位置參數(shù)和關(guān)鍵字參數(shù)分別以元組和dict形式給出。

這是一個(gè)專門函數(shù),其目的是被放入 tp_call 槽位或是用于 tp_call 的實(shí)現(xiàn)。 它不會檢查 Py_TPFLAGS_HAVE_VECTORCALL 旗標(biāo)并且它不會回退到 tp_call。

這個(gè)函數(shù)不是 limited API 的一部分。

3.8 新版功能.

調(diào)用對象的 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.

下表總結(jié)了可用的功能; 請參閱各個(gè)文檔以了解詳細(xì)信息。

函數(shù)

可調(diào)用對象(Callable)

args

kwargs

PyObject_Call()

PyObject *

元組

dict/NULL

PyObject_CallNoArgs()

PyObject *

---

---

PyObject_CallOneArg()

PyObject *

1個(gè)對象

---

PyObject_CallObject()

PyObject *

元組/NULL

---

PyObject_CallFunction()

PyObject *

format

---

PyObject_CallMethod()

對象 + char*

format

---

PyObject_CallFunctionObjArgs()

PyObject *

可變參數(shù)

---

PyObject_CallMethodObjArgs()

對象 + 名稱

可變參數(shù)

---

PyObject_CallMethodNoArgs()

對象 + 名稱

---

---

PyObject_CallMethodOneArg()

對象 + 名稱

1個(gè)對象

---

PyObject_Vectorcall()

PyObject *

vectorcall

vectorcall

PyObject_VectorcallDict()

PyObject *

vectorcall

dict/NULL

PyObject_VectorcallMethod()

參數(shù) + 名稱

vectorcall

vectorcall

PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)?
Return value: New reference. Part of the Stable ABI.

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable,附帶由元組 args 所給出的參數(shù),以及由字典 kwargs 所給出的關(guān)鍵字參數(shù)。

args 必須不為 NULL;如果不想要參數(shù)請使用一個(gè)空元組。 如果不想要關(guān)鍵字參數(shù),則 kwargs 可以為 NULL。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL

這等價(jià)于 Python 表達(dá)式 callable(*args, **kwargs)。

PyObject *PyObject_CallNoArgs(PyObject *callable)?
Part of the Stable ABI since version 3.10.

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable 并不附帶任何參數(shù)。 這是不帶參數(shù)調(diào)用 Python 可調(diào)用對象的最有效方式。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

3.9 新版功能.

PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)?

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable 并附帶恰好 1 個(gè)位置參數(shù) arg 而沒有關(guān)鍵字參數(shù)。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這個(gè)函數(shù)不是 limited API 的一部分。

3.9 新版功能.

PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)?
Return value: New reference. Part of the Stable ABI.

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable,附帶由元組 args 所給出的參數(shù)。 如果不想要傳入?yún)?shù),則 args 可以為 NULL

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這等價(jià)于 Python 表達(dá)式 callable(*args)

PyObject *PyObject_CallFunction(PyObject *callable, const char *format, ...)?
Return value: New reference. Part of the Stable ABI.

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable,附帶可變數(shù)量的 C 參數(shù)。 這些 C 參數(shù)使用 Py_BuildValue() 風(fēng)格的格式字符串來描述。 format 可以為 NULL,表示沒有提供任何參數(shù)。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這等價(jià)于 Python 表達(dá)式 callable(*args)。

請注意如果你只傳入 PyObject* 參數(shù),則 PyObject_CallFunctionObjArgs() 是更快速的選擇。

在 3.4 版更改: 這個(gè) format 類型已從 char * 更改。

PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)?
Return value: New reference. Part of the Stable ABI.

調(diào)用 obj 對象中名為 name 的方法并附帶可變數(shù)量的 C 參數(shù)。 這些 C 參數(shù)由 Py_BuildValue() 格式字符串來描述并應(yīng)當(dāng)生成一個(gè)元組。

格式可以為 NULL ,表示未提供任何參數(shù)。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這和Python表達(dá)式``obj.name(arg1, arg2, ...)``是一樣的。

請注意如果你只傳入 PyObject* 參數(shù),則 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.

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable,附帶可變數(shù)量的 PyObject* 參數(shù)。 這些參數(shù)是以 NULL 之后可變數(shù)量的形參的形式提供的。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這和Python表達(dá)式``callable(arg1, arg2, ...)``是一樣的。

PyObject *PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)?
Return value: New reference. Part of the Stable ABI.

調(diào)用 Python 對象 obj 中的一個(gè)方法,其中方法名稱由 name 中的 Python 字符串對象給出。 它將附帶可變數(shù)量的 PyObject* 參數(shù)被調(diào)用。 這些參數(shù)是以 NULL 之后可變數(shù)量的形參的形式提供的。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

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

調(diào)用 Python 對象 obj 中的一個(gè)方法并不附帶任何參數(shù),其中方法名稱由 name 中的 Python 字符串對象給出。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這個(gè)函數(shù)不是 limited API 的一部分。

3.9 新版功能.

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

調(diào)用 Python 對象 obj 中的一個(gè)方法并附帶單個(gè)位置參數(shù) arg,其中方法名稱由 name 中的 Python 字符串對象給出。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這個(gè)函數(shù)不是 limited API 的一部分。

3.9 新版功能.

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

調(diào)用一個(gè)可調(diào)用的 Python 對象 callable。 附帶的參數(shù)與 vectorcallfunc 相同。 如果 callable 支持 vectorcall,則它會直接調(diào)用存放在 callable 中的 vectorcall 函數(shù)。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL

這個(gè)函數(shù)不是 limited API 的一部分。

3.9 新版功能.

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

調(diào)用 callable 并附帶與在 vectorcall 協(xié)議中傳入的完全相同的位置參數(shù),但會加上以字典 kwdict 形式傳入的關(guān)鍵字參數(shù)。 args 數(shù)組將只包含位置參數(shù)。

無論在內(nèi)部使用哪種協(xié)議,都需要進(jìn)行參數(shù)的轉(zhuǎn)換。 因此,此函數(shù)應(yīng)當(dāng)僅在調(diào)用方已經(jīng)擁有作為關(guān)鍵字參數(shù)的字典,但沒有作為位置參數(shù)的元組時(shí)才被使用。

這個(gè)函數(shù)不是 limited API 的一部分。

3.9 新版功能.

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

使用 vectorcall 調(diào)用慣例來調(diào)用一個(gè)方法。 方法的名稱以 Python 字符串 name 的形式給出。 調(diào)用方法的對象為 args[0],而 args 數(shù)組從 args[1] 開始的部分則代表調(diào)用的參數(shù)。 必須傳入至少一個(gè)位置參數(shù)。 nargsf 為包括 args[0] 在內(nèi)的位置參數(shù)的數(shù)量,如果 args[0] 的值可能被臨時(shí)改變則要再加上 PY_VECTORCALL_ARGUMENTS_OFFSET。 關(guān)鍵字參數(shù)可以像在 PyObject_Vectorcall() 中一樣被傳入。

如果對象具有 Py_TPFLAGS_METHOD_DESCRIPTOR 特性,此函數(shù)將調(diào)用調(diào)用未綁定的方法對象并附帶完整的 args vector 作為參數(shù)。

成功時(shí)返回結(jié)果,在失敗時(shí)拋出一個(gè)異常并返回 NULL。

這個(gè)函數(shù)不是 limited API 的一部分。

3.9 新版功能.

調(diào)用支持 API?

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

確定對象 o 是可調(diào)對象。如果對象是可調(diào)對象則返回 1 ,其他情況返回 0 。這個(gè)函數(shù)不會調(diào)用失敗。