調用協(xié)議?

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

tp_call 協(xié)議?

設置 tp_call 的類的實例都是可調用的。 槽位的簽名為:

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

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

這個約定不僅被*tp_call*使用: tp_newtp_init 也這樣傳遞參數。

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

Vectorcall 協(xié)議?

3.9 新版功能.

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

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

警告

一個支持 Vectorcall 的類 必須 也實現具有相同語義的 tp_call。

如果一個類的vectorcall比*tp_call*慢,就不應該實現vectorcall。例如,如果被調用者需要將參數轉換為args 元組和kwargs dict,那么實現vectorcall就沒有意義。

類可以通過啟用 Py_TPFLAGS_HAVE_VECTORCALL 標志并將 tp_vectorcall_offset 設置為對象結構中的 vectorcallfunc 的 offset 來實現 vectorcall 協(xié)議。這是一個指向具有以下簽名的函數的指針:

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

  • args 是一個C語言數組,由位置參數和后面的

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

  • nargsf 是位置參數的數量加上可能的

    PY_VECTORCALL_ARGUMENTS_OFFSET 標志。 要從 nargsf 獲得實際的位置參數數,請使用 PyVectorcall_NARGS()

  • kwnames 是一包含所有關鍵字名稱的元組。

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

PY_VECTORCALL_ARGUMENTS_OFFSET?

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

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

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

要調用一個實現了 vectorcall 的對象,請使用某個 call 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 函數以便將來擴展。

這個函數不是 limited API 的一部分。

3.8 新版功能.

vectorcallfunc PyVectorcall_Function(PyObject *op)?

如果*op*不支持vectorcall協(xié)議(要么是因為類型不支持,要么是因為具體實例不支持),返回*NULL*。否則,返回存儲在*op*中的vectorcall函數指針。這個函數從不觸發(fā)異常。

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

這個函數不是 limited API 的一部分。

3.8 新版功能.

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

調用*可調對象*的 vectorcallfunc,其位置參數和關鍵字參數分別以元組和dict形式給出。

這是一個專門函數,其目的是被放入 tp_call 槽位或是用于 tp_call 的實現。 它不會檢查 Py_TPFLAGS_HAVE_VECTORCALL 旗標并且它不會回退到 tp_call。

這個函數不是 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.

下表總結了可用的功能; 請參閱各個文檔以了解詳細信息。

函數

可調用對象(Callable)

args

kwargs

PyObject_Call()

PyObject *

元組

dict/NULL

PyObject_CallNoArgs()

PyObject *

---

---

PyObject_CallOneArg()

PyObject *

1個對象

---

PyObject_CallObject()

PyObject *

元組/NULL

---

PyObject_CallFunction()

PyObject *

format

---

PyObject_CallMethod()

對象 + char*

format

---

PyObject_CallFunctionObjArgs()

PyObject *

可變參數

---

PyObject_CallMethodObjArgs()

對象 + 名稱

可變參數

---

PyObject_CallMethodNoArgs()

對象 + 名稱

---

---

PyObject_CallMethodOneArg()

對象 + 名稱

1個對象

---

PyObject_Vectorcall()

PyObject *

vectorcall

vectorcall

PyObject_VectorcallDict()

PyObject *

vectorcall

dict/NULL

PyObject_VectorcallMethod()

參數 + 名稱

vectorcall

vectorcall

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

調用一個可調用的 Python 對象 callable,附帶由元組 args 所給出的參數,以及由字典 kwargs 所給出的關鍵字參數。

args 必須不為 NULL;如果不想要參數請使用一個空元組。 如果不想要關鍵字參數,則 kwargs 可以為 NULL。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

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

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

調用一個可調用的 Python 對象 callable 并不附帶任何參數。 這是不帶參數調用 Python 可調用對象的最有效方式。

成功時返回結果,在失敗時拋出一個異常并返回 NULL

3.9 新版功能.

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

調用一個可調用的 Python 對象 callable 并附帶恰好 1 個位置參數 arg 而沒有關鍵字參數。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這個函數不是 limited API 的一部分。

3.9 新版功能.

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

調用一個可調用的 Python 對象 callable,附帶由元組 args 所給出的參數。 如果不想要傳入參數,則 args 可以為 NULL。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這等價于 Python 表達式 callable(*args)。

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

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

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這等價于 Python 表達式 callable(*args)。

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

在 3.4 版更改: 這個 format 類型已從 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() 格式字符串來描述并應當生成一個元組。

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

成功時返回結果,在失敗時拋出一個異常并返回 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.

調用一個可調用的 Python 對象 callable,附帶可變數量的 PyObject* 參數。 這些參數是以 NULL 之后可變數量的形參的形式提供的。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

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

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

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

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

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

調用 Python 對象 obj 中的一個方法并不附帶任何參數,其中方法名稱由 name 中的 Python 字符串對象給出。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這個函數不是 limited API 的一部分。

3.9 新版功能.

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

調用 Python 對象 obj 中的一個方法并附帶單個位置參數 arg,其中方法名稱由 name 中的 Python 字符串對象給出。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這個函數不是 limited API 的一部分。

3.9 新版功能.

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

調用一個可調用的 Python 對象 callable。 附帶的參數與 vectorcallfunc 相同。 如果 callable 支持 vectorcall,則它會直接調用存放在 callable 中的 vectorcall 函數。

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這個函數不是 limited API 的一部分。

3.9 新版功能.

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

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

無論在內部使用哪種協(xié)議,都需要進行參數的轉換。 因此,此函數應當僅在調用方已經擁有作為關鍵字參數的字典,但沒有作為位置參數的元組時才被使用。

這個函數不是 limited API 的一部分。

3.9 新版功能.

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

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

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

成功時返回結果,在失敗時拋出一個異常并返回 NULL。

這個函數不是 limited API 的一部分。

3.9 新版功能.

調用支持 API?

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

確定對象 o 是可調對象。如果對象是可調對象則返回 1 ,其他情況返回 0 。這個函數不會調用失敗。