调用协议¶
CPython 支持两种不同的调用协议:tp_call 和矢量调用。
tp_call 协议¶
设置 tp_call 的类的实例都是可调用的。 槽位的签名为:
PyObject *tp_call(PyObject *callable, PyObject *args, PyObject *kwargs);
一个调用是用一个元组表示位置参数,用一个dict表示关键字参数,类似于Python代码中的``callable(args, **kwargs)``。*args*必须是非空的(如果没有参数,会使用一个空元组),但如果没有关键字参数,*kwargs*可以是*NULL。
这个约定不仅被*tp_call*使用: tp_new 和 tp_init 也这样传递参数。
使用 PyObject_Call() 或其他 调用 API 来调用一个对象。
Vectorcall 协议¶
3.9 新版功能.
vectorcall 协议是在 PEP 590 被引入的,它是使调用函数更加有效的附加协议。
作为经验法则,如果可调用程序支持 vectorcall,CPython 会更倾向于内联调用。 然而,这并不是一个硬性规定。 此外,一些第三方扩展直接使用 tp_call (而不是使用 PyObject_Call())。 因此,一个支持 vectorcall 的类也必须实现 tp_call。 此外,无论使用哪种协议,可调对象的行为都必须是相同的。 推荐的方法是将 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 协议。这是一个指向具有以下签名的函数的指针:
-
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() 通常是最有效的。
注解
In CPython 3.8, the vectorcall API and related functions were available
provisionally under names with a leading underscore:
_PyObject_Vectorcall, _Py_TPFLAGS_HAVE_VECTORCALL,
_PyObject_VectorcallMethod, _PyVectorcall_Function,
_PyObject_CallOneArg, _PyObject_CallMethodNoArgs,
_PyObject_CallMethodOneArg.
Additionally, PyObject_VectorcallDict was available as
_PyObject_FastCallDict.
The old names are still defined as aliases of the new, non-underscored names.
递归控制¶
在使用 tp_call 时,被调用者不必担心 递归: CPython 对于使用 tp_call 进行的调用会使用 Py_EnterRecursiveCall() 和 Py_LeaveRecursiveCall()。
For efficiency, this is not the case for calls done using vectorcall: the callee should use Py_EnterRecursiveCall and Py_LeaveRecursiveCall if needed.
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协议(要么是因为类型不支持,要么是因为具体实例不支持),返回*NULL*。否则,返回存储在*op*中的vectorcall函数指针。这个函数从不触发异常。
This is mostly useful to check whether or not op supports vectorcall, which can be done by checking
PyVectorcall_Function(op) != NULL.这个函数不是 limited API 的一部分。
3.8 新版功能.
-
PyObject*
PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)¶ 调用*可调对象*的
vectorcallfunc,其位置参数和关键字参数分别以元组和dict形式给出。This is a specialized function, intended to be put in the
tp_callslot or be used in an implementation oftp_call. It does not check thePy_TPFLAGS_HAVE_VECTORCALLflag and it does not fall back totp_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 litle conversion as possible, pick one that best fits the format of data you have available.
下表总结了可用的功能; 请参阅各个文档以了解详细信息。
函数 |
可调用对象(Callable) |
args |
kwargs |
|---|---|---|---|
|
元组 |
dict/ |
|
|
--- |
--- |
|
|
1个对象 |
--- |
|
|
元组/ |
--- |
|
|
format |
--- |
|
对象 + |
format |
--- |
|
|
可变参数 |
--- |
|
对象 + 名称 |
可变参数 |
--- |
|
对象 + 名称 |
--- |
--- |
|
对象 + 名称 |
1个对象 |
--- |
|
|
vectorcall |
vectorcall |
|
|
vectorcall |
dict/ |
|
参数 + 名称 |
vectorcall |
vectorcall |
-
PyObject*
PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)¶ - Return value: New reference.
Call a callable Python object callable, with arguments given by the tuple args, and named arguments given by the dictionary kwargs.
args must not be NULL; use an empty tuple if no arguments are needed. If no named arguments are needed, kwargs can be NULL.
成功时返回结果,在失败时抛出一个异常并返回 NULL。
这等价于 Python 表达式
callable(*args, **kwargs)。
-
PyObject*
PyObject_CallNoArgs(PyObject *callable)¶ Call a callable Python object callable without any arguments. It is the most efficient way to call a callable Python object without any argument.
成功时返回结果,在失败时抛出一个异常并返回 NULL。
3.9 新版功能.
-
PyObject*
PyObject_CallOneArg(PyObject *callable, PyObject *arg)¶ Call a callable Python object callable with exactly 1 positional argument arg and no keyword arguments.
成功时返回结果,在失败时抛出一个异常并返回 NULL。
这个函数不是 limited API 的一部分。
3.9 新版功能.
-
PyObject*
PyObject_CallObject(PyObject *callable, PyObject *args)¶ - Return value: New reference.
Call a callable Python object callable, with arguments given by the tuple args. If no arguments are needed, then args can be NULL.
成功时返回结果,在失败时抛出一个异常并返回 NULL。
这等价于 Python 表达式
callable(*args)。
-
PyObject*
PyObject_CallFunction(PyObject *callable, const char *format, ...)¶ - Return value: New reference.
Call a callable Python object callable, with a variable number of C arguments. The C arguments are described using a
Py_BuildValue()style format string. The format can be NULL, indicating that no arguments are provided.成功时返回结果,在失败时抛出一个异常并返回 NULL。
这等价于 Python 表达式
callable(*args)。Note that if you only pass
PyObject *args,PyObject_CallFunctionObjArgs()is a faster alternative.在 3.4 版更改: 这个 format 类型已从
char *更改。
-
PyObject*
PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)¶ - Return value: New reference.
Call the method named name of object obj with a variable number of C arguments. The C arguments are described by a
Py_BuildValue()format string that should produce a tuple.格式可以为 NULL ,表示未提供任何参数。
成功时返回结果,在失败时抛出一个异常并返回 NULL。
这和Python表达式``obj.name(arg1, arg2, ...)``是一样的。
Note that if you only pass
PyObject *args,PyObject_CallMethodObjArgs()is a faster alternative.在 3.4 版更改: The types of name and format were changed from
char *.
-
PyObject*
PyObject_CallFunctionObjArgs(PyObject *callable, ...)¶ - Return value: New reference.
Call a callable Python object callable, with a variable number of
PyObject *arguments. The arguments are provided as a variable number of parameters followed by NULL.成功时返回结果,在失败时抛出一个异常并返回 NULL。
这和Python表达式``callable(arg1, arg2, ...)``是一样的。
-
PyObject*
PyObject_CallMethodObjArgs(PyObject *obj, PyObject *name, ...)¶ - Return value: New reference.
Call a method of the Python object obj, where the name of the method is given as a Python string object in name. It is called with a variable number of
PyObject *arguments. The arguments are provided as a variable number of parameters followed by NULL.成功时返回结果,在失败时抛出一个异常并返回 NULL。
-
PyObject*
PyObject_CallMethodNoArgs(PyObject *obj, PyObject *name)¶ Call a method of the Python object obj without arguments, where the name of the method is given as a Python string object in name.
成功时返回结果,在失败时抛出一个异常并返回 NULL。
这个函数不是 limited API 的一部分。
3.9 新版功能.
-
PyObject*
PyObject_CallMethodOneArg(PyObject *obj, PyObject *name, PyObject *arg)¶ Call a method of the Python object obj with a single positional argument arg, where the name of the method is given as a Python string object in name.
成功时返回结果,在失败时抛出一个异常并返回 NULL。
这个函数不是 limited API 的一部分。
3.9 新版功能.
-
PyObject*
PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶ Call a callable Python object callable. The arguments are the same as for
vectorcallfunc. If callable supports vectorcall, this directly calls the vectorcall function stored in callable.成功时返回结果,在失败时抛出一个异常并返回 NULL。
这个函数不是 limited API 的一部分。
3.9 新版功能.
-
PyObject*
PyObject_VectorcallDict(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwdict)¶ Call callable with positional arguments passed exactly as in the vectorcall protocol, but with keyword arguments passed as a dictionary kwdict. The args array contains only the positional arguments.
Regardless of which protocol is used internally, a conversion of arguments needs to be done. Therefore, this function should only be used if the caller already has a dictionary ready to use for the keyword arguments, but not a tuple for the positional arguments.
这个函数不是 limited API 的一部分。
3.9 新版功能.
-
PyObject*
PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)¶ Call a method using the vectorcall calling convention. The name of the method is given as a Python string name. The object whose method is called is args[0], and the args array starting at args[1] represents the arguments of the call. There must be at least one positional argument. nargsf is the number of positional arguments including args[0], plus
PY_VECTORCALL_ARGUMENTS_OFFSETif the value ofargs[0]may temporarily be changed. Keyword arguments can be passed just like inPyObject_Vectorcall().If the object has the
Py_TPFLAGS_METHOD_DESCRIPTORfeature, this will call the unbound method object with the full args vector as arguments.成功时返回结果,在失败时抛出一个异常并返回 NULL。
这个函数不是 limited API 的一部分。
3.9 新版功能.
