Протокол вызова

CPython поддерживает два различных протокола вызова: tp_call и vectorcall.

Протокол tp_call

Экземпляры классов, задающих tp_call, являются вызываемыми. Сигнатура слота выглядит так:

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

Для вызова используется кортеж для позиционных аргументов и dict для аргументов ключевых слов, аналогично callable(*args, **kwargs) в коде Python. args должен быть не NULL (используйте пустой кортеж, если нет аргументов), но kwargs может быть NULL, если нет аргументов-ключей.

Это соглашение используется не только в tp_call: tp_new и tp_init также передают аргументы таким образом.

Чтобы вызвать объект, используйте PyObject_Call() или другой call API.

Протокол Vectorcall

Added in version 3.9.

Протокол vectorcall был представлен в PEP 590 как дополнительный протокол для повышения эффективности вызовов.

Как правило, CPython предпочитает использовать vectorcall для внутренних вызовов, если вызываемый объект поддерживает его. Однако это не является жестким правилом. Кроме того, некоторые сторонние расширения используют tp_call напрямую (а не с помощью PyObject_Call()). Поэтому класс, поддерживающий vectorcall, должен также реализовать tp_call. Более того, вызываемый объект должен вести себя одинаково независимо от того, какой протокол используется. Рекомендуемый способ добиться этого - установить tp_call в PyVectorcall_Call(). Это следует повторить:

Предупреждение

Класс, поддерживающий vectorcall, должен также реализовать tp_call с той же семантикой.

Изменено в версии 3.12: Флаг Py_TPFLAGS_HAVE_VECTORCALL теперь снимается с класса при переназначении его метода __call__(). (Это внутренне устанавливает только tp_call и, таким образом, может заставить его вести себя иначе, чем функция vectorcall). В более ранних версиях Python vectorcall можно было использовать только с immutable или статическими типами.

Класс не должен реализовывать vectorcall, если это будет медленнее, чем tp_call. Например, если вызывающему классу все равно нужно преобразовывать аргументы в кортеж args и дикту kwargs, то нет смысла реализовывать vectorcall.

Классы могут реализовать протокол vectorcall, включив флаг Py_TPFLAGS_HAVE_VECTORCALL и установив tp_vectorcall_offset на смещение внутри структуры объекта, где появляется vectorcallfunc. Это указатель на функцию со следующей сигнатурой:

typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
Часть Стабильный ABI с версии 3.12.
  • callable - это вызываемый объект.

  • args - это массив C, состоящий из позиционных аргументов, за которыми следует

    значения ключевых слов-аргументов. Это может быть NULL, если аргументов нет.

  • nargsf - это число позиционных аргументов плюс, возможно, число

    флаг PY_VECTORCALL_ARGUMENTS_OFFSET. Чтобы получить фактическое количество позиционных аргументов из nargsf, используйте PyVectorcall_NARGS().

  • kwnames - кортеж, содержащий имена аргументов ключевых слов;

    другими словами, ключи диктанта kwargs. Эти имена должны быть строками (экземплярами str или подклассами) и должны быть уникальными. Если аргументов-ключей нет, то вместо kwnames может быть NULL.

PY_VECTORCALL_ARGUMENTS_OFFSET
Часть Стабильный ABI с версии 3.12.

Если этот флаг установлен в аргументе векторного вызова nargsf, то вызывающему разрешается временно изменить args[-1]. Другими словами, args указывает на аргумент 1 (а не 0) в выделенном векторе. Перед возвратом вызывающий должен восстановить значение args[-1].

Для PyObject_VectorcallMethod() этот флаг означает, что args[0] может быть изменен.

Когда это можно сделать дешево (без дополнительного выделения), вызывающим рекомендуется использовать PY_VECTORCALL_ARGUMENTS_OFFSET. Это позволит вызываемым элементам, таким как связанные методы, выполнять свои последующие вызовы (которые включают добавленный аргумент self) очень эффективно.

Added in version 3.8.

Для вызова объекта, реализующего vectorcall, используйте функцию call API, как и для любого другого вызываемого объекта. PyObject_Vectorcall() обычно оказывается наиболее эффективной.

Управление рекурсией

При использовании tp_call каллегам не нужно беспокоиться о recursion: CPython использует Py_EnterRecursiveCall() и Py_LeaveRecursiveCall() для вызовов, сделанных с помощью tp_call.

Для эффективности, это не относится к вызовам, выполняемым с помощью vectorcall: вызывающий должен использовать Py_EnterRecursiveCall и Py_LeaveRecursiveCall, если это необходимо.

API поддержки Vectorcall

Py_ssize_t PyVectorcall_NARGS(size_t nargsf)
Часть Стабильный ABI с версии 3.12.

Учитывая аргумент vectorcall nargsf, возвращает фактическое количество аргументов. В настоящее время эквивалентно:

(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

Однако следует использовать функцию PyVectorcall_NARGS, чтобы предусмотреть возможность расширения в будущем.

Added in version 3.8.

vectorcallfunc PyVectorcall_Function(PyObject *op)

Если op не поддерживает протокол vectorcall (либо потому что тип не поддерживает, либо потому что конкретный экземпляр не поддерживает), верните NULL. В противном случае возвращается указатель функции vectorcall, хранящийся в op. Эта функция никогда не вызывает исключений.

В основном это полезно для проверки того, поддерживает ли op vectorcall, что можно сделать, проверив PyVectorcall_Function(op) != NULL.

Added in version 3.9.

PyObject *PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict)
Часть Стабильный ABI с версии 3.12.

Вызов функции callable vectorcallfunc с позиционными и ключевыми аргументами, заданными в виде кортежа и dict, соответственно.

Это специализированная функция, предназначенная для размещения в слоте tp_call или использования в реализации tp_call. Она не проверяет флаг Py_TPFLAGS_HAVE_VECTORCALL и не возвращается к tp_call.

Added in version 3.8.

API вызова объектов

Для вызова объекта Python доступны различные функции. Каждая из них преобразует свои аргументы в формат, поддерживаемый вызываемым объектом - либо tp_call, либо vectorcall. Чтобы делать как можно меньше преобразований, выберите ту, которая лучше всего подходит к формату имеющихся у вас данных.

В следующей таблице кратко описаны доступные функции; подробности см. в документации на конкретные устройства.

Функция

вызываемый

args

kwargs

PyObject_Call()

PyObject *

кортеж

dict/NULL

PyObject_CallNoArgs()

PyObject *

PyObject_CallOneArg()

PyObject *

1 объект

PyObject_CallObject()

PyObject *

кортеж/NULL

PyObject_CallFunction()

PyObject *

формат

PyObject_CallMethod()

obj + char*

формат

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)
Возвращаемое значение: Новая ссылка. Часть Стабильный ABI.

Вызов вызываемого объекта Python callable, аргументы которого задаются кортежем args, а именованные аргументы - словарем kwargs.

args не должен быть NULL; используйте пустой кортеж, если аргументы не нужны. Если именованные аргументы не нужны, kwargs может быть NULL.

Возвращает результат вызова в случае успеха, или вызывает исключение и возвращает NULL в случае неудачи.

Это эквивалентно выражению Python: callable(*args, **kwargs).

PyObject *PyObject_CallNoArgs(PyObject *callable)
Возвращаемое значение: Новая ссылка. Часть Стабильный ABI с версии 3.10.

Вызов вызываемого объекта Python callable без каких-либо аргументов. Это самый эффективный способ вызова вызываемого объекта Python без аргументов.

Возвращает результат вызова в случае успеха, или вызывает исключение и возвращает NULL в случае неудачи.

Added in version 3.9.

PyObject *PyObject_CallOneArg(PyObject *callable, PyObject *arg)
Возвращаемое значение: Новая ссылка.

Вызов вызываемого объекта Python callable с ровно 1 позиционным аргументом arg и без ключевых аргументов.

Возвращает результат вызова в случае успеха, или вызывает исключение и возвращает NULL в случае неудачи.

Added in version 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: Тип формата был изменен с char *.

PyObject *PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)
Возвращаемое значение: Новая ссылка. Часть Стабильный ABI.

Вызов метода с именем name объекта obj с переменным количеством аргументов C. Аргументы 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 в случае неудачи.

Added in version 3.9.

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

Вызов метода объекта Python obj с одним позиционным аргументом arg, где имя метода задается как строковый объект Python в name.

Возвращает результат вызова в случае успеха, или вызывает исключение и возвращает NULL в случае неудачи.

Added in version 3.9.

PyObject *PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames)
Часть Стабильный ABI с версии 3.12.

Вызов вызываемого объекта Python callable. Аргументы те же, что и для vectorcallfunc. Если callable поддерживает функцию vectorcall, то вызывается непосредственно функция vectorcall, хранящаяся в callable.

Возвращает результат вызова в случае успеха, или вызывает исключение и возвращает NULL в случае неудачи.

Added in version 3.9.

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

Вызов callable с позиционными аргументами, передаваемыми точно так же, как в протоколе vectorcall, но с аргументами в виде ключевых слов, передаваемых в виде словаря kwdict. Массив args содержит только позиционные аргументы.

Независимо от того, какой протокол используется внутри, необходимо выполнить преобразование аргументов. Поэтому эту функцию следует использовать только в том случае, если у вызывающей стороны уже есть готовый словарь для ключевых аргументов, но не кортеж для позиционных аргументов.

Added in version 3.9.

PyObject *PyObject_VectorcallMethod(PyObject *name, PyObject *const *args, size_t nargsf, PyObject *kwnames)
Часть Стабильный ABI с версии 3.12.

Вызывает метод, используя соглашение о вызове vectorcall. Имя метода задается в виде Python-строки name. Объект, метод которого вызывается, - это args[0], а массив args, начинающийся с args[1], представляет собой аргументы вызова. Должен быть хотя бы один позиционный аргумент. nargsf - это количество позиционных аргументов, включая args[0], плюс PY_VECTORCALL_ARGUMENTS_OFFSET, если значение args[0] может быть временно изменено. Аргументы с ключевыми словами могут передаваться так же, как и в PyObject_Vectorcall().

Если объект имеет свойство Py_TPFLAGS_METHOD_DESCRIPTOR, это вызовет несвязанный объект метода с полным вектором args в качестве аргументов.

Возвращает результат вызова в случае успеха, или вызывает исключение и возвращает NULL в случае неудачи.

Added in version 3.9.

API поддержки вызовов

int PyCallable_Check(PyObject *o)
Часть Стабильный ABI.

Определите, является ли объект o вызываемым. Возвращает 1, если объект является вызываемым, и 0 в противном случае. Эта функция всегда успешна.