呼叫協議¶
CPython 支援兩種不同的呼叫協議:tp_call 和 vectorcall。
tp_call 協議¶
設定 tp_call
的類的例項是可呼叫的。槽的簽名是
PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);
呼叫使用元組作為位置引數,使用字典作為關鍵字引數,類似於 Python 程式碼中的 callable(*args, **kwargs)
。args 必須是非 NULL(如果沒有引數則使用空元組),但如果沒有關鍵字引數,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 元組和 kwargs 字典,那麼實現 vectorcall 就沒有意義了。
類可以透過啟用 Py_TPFLAGS_HAVE_VECTORCALL
標誌並將 tp_vectorcall_offset
設定為物件結構內 vectorcallfunc 出現的偏移量來實現 vectorcall 協議。這是指向具有以下簽名的函式的指標
-
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- 自 3.12 版本以來是 穩定 ABI 的一部分。
callable 是被呼叫的物件。
- args 是一個 C 陣列,由位置引數後跟
關鍵字引數的值組成。如果沒有引數,則可以為 NULL。
- nargsf 是位置引數的數量,加上可能為
PY_VECTORCALL_ARGUMENTS_OFFSET
標誌。要從 nargsf 獲取位置引數的實際數量,請使用PyVectorcall_NARGS()
。
- kwnames 是一個包含關鍵字引數名稱的元組;
換句話說,是 kwargs 字典的鍵。這些名稱必須是字串(
str
的例項或子類),並且必須是唯一的。如果沒有關鍵字引數,則 kwnames 可以改為 NULL。
-
PY_VECTORCALL_ARGUMENTS_OFFSET¶
- 自 3.12 版本以來是 穩定 ABI 的一部分。
如果在 vectorcall nargsf 引數中設定了此標誌,則允許被呼叫者臨時更改
args[-1]
。換句話說,args 指向已分配向量中的引數 1(而不是 0)。被呼叫者必須在返回之前恢復args[-1]
的值。對於
PyObject_VectorcallMethod()
,此標誌表示可以更改args[0]
。只要他們能夠以較低的成本(無需額外分配)執行此操作,就鼓勵呼叫者使用
PY_VECTORCALL_ARGUMENTS_OFFSET
。這樣做將允許諸如繫結方法之類的可呼叫物件非常有效地進行後續呼叫(包括前置的 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)¶
- 自 3.12 版本以來是 穩定 ABI 的一部分。
給定一個 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)¶
- 自 3.12 版本以來是 穩定 ABI 的一部分。
使用元組和字典中分別給出的位置引數和關鍵字引數呼叫 callable 的
vectorcallfunc
。這是一個專門的函式,旨在放入
tp_call
插槽或在tp_call
的實現中使用。它不檢查Py_TPFLAGS_HAVE_VECTORCALL
標誌,並且不會回退到tp_call
。在 3.8 版本中新增。
物件呼叫 API¶
有各種函式可用於呼叫 Python 物件。每個函式都會將其引數轉換為被呼叫物件支援的約定——tp_call 或 vectorcall。為了儘可能少地進行轉換,請選擇最適合您可用資料格式的函式。
下表總結了可用的函式;請參閱各個文件瞭解詳細資訊。
函式 |
可呼叫物件 |
args |
kwargs |
---|---|---|---|
|
元組 |
字典/ |
|
|
— |
— |
|
|
1 個物件 |
— |
|
|
元組/ |
— |
|
|
格式 |
— |
|
obj + |
格式 |
— |
|
|
可變引數 |
— |
|
obj + 名稱 |
可變引數 |
— |
|
obj + 名稱 |
— |
— |
|
obj + 名稱 |
1 個物件 |
— |
|
|
vectorcall |
vectorcall |
|
|
vectorcall |
字典/ |
|
arg + 名稱 |
vectorcall |
vectorcall |
-
PyObject *PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)¶
- 返回值:新引用。是穩定 ABI的一部分。
呼叫可呼叫的 Python 物件 callable,引數由元組 args 給出,命名引數由字典 kwargs 給出。
args 不能為 NULL;如果不需要引數,請使用空元組。如果不需要命名引數,kwargs 可以為 NULL。
成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
這等效於 Python 表示式:
callable(*args, **kwargs)
。
-
PyObject *PyObject_CallNoArgs(PyObject *callable)¶
- 返回值:新引用。自 3.10 版本起是 穩定 ABI的一部分。
呼叫一個不帶任何引數的可呼叫 Python 物件 callable。這是呼叫不帶任何引數的可呼叫 Python 物件的最有效方法。
成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
在 3.9 版本中新增。
-
PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)¶
- 返回值:新引用。
呼叫一個可呼叫的 Python 物件 callable,它具有恰好 1 個位置引數 arg 且沒有關鍵字引數。
成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
在 3.9 版本中新增。
-
PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)¶
- 返回值:新引用。是穩定 ABI的一部分。
呼叫一個可呼叫的 Python 物件 callable,引數由元組 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的一部分。
使用可變數量的 C 引數呼叫物件 obj 的名為 name 的方法。C 引數由
Py_BuildValue()
格式字串描述,該字串應生成一個元組。格式可以為 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 的一個方法,其中方法名稱以 Python 字串物件的形式在 name 中給出。它使用可變數量的 PyObject* 引數呼叫。這些引數以可變數量的引數形式提供,並以 NULL 結尾。
成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
-
PyObject *PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)¶
呼叫 Python 物件 obj 的一個不帶引數的方法,其中方法名稱以 Python 字串物件的形式在 name 中給出。
成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
在 3.9 版本中新增。
-
PyObject *PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)¶
呼叫 Python 物件 obj 的一個帶單個位置引數 arg 的方法,其中方法名稱以 Python 字串物件的形式在 name 中給出。
成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
在 3.9 版本中新增。
-
PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- 自 3.12 版本以來是 穩定 ABI 的一部分。
呼叫一個可呼叫的 Python 物件 callable。引數與
vectorcallfunc
的引數相同。如果 callable 支援 vectorcall,則此函式直接呼叫儲存在 callable 中的 vectorcall 函式。成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
在 3.9 版本中新增。
-
PyObject *PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)¶
呼叫 callable,其中位置引數的傳遞方式與 vectorcall 協議中完全相同,但關鍵字引數以字典 kwdict 的形式傳遞。args 陣列僅包含位置引數。
無論內部使用哪種協議,都需要進行引數轉換。因此,只有當呼叫者已經有一個準備好用於關鍵字引數的字典,但沒有用於位置引數的元組時,才應使用此函式。
在 3.9 版本中新增。
-
PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶
- 自 3.12 版本以來是 穩定 ABI 的一部分。
使用 vectorcall 呼叫約定呼叫方法。方法名稱以 Python 字串 name 的形式給出。呼叫方法的物件是 args[0],並且從 args[1] 開始的 args 陣列表示呼叫的引數。必須至少有一個位置引數。nargsf 是位置引數的數量,包括 args[0],加上
PY_VECTORCALL_ARGUMENTS_OFFSET
,如果args[0]
的值可能會被臨時更改。關鍵字引數的傳遞方式與PyObject_Vectorcall()
中相同。如果物件具有
Py_TPFLAGS_METHOD_DESCRIPTOR
特性,則這將使用完整的 args 向量作為引數來呼叫未繫結的方法物件。成功時返回呼叫的結果,失敗時引發異常並返回 NULL。
在 3.9 版本中新增。