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

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

Протокол tp_call

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

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

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

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

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

Протокол Vectorcall

Добавлено в версии 3.9.

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

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

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

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

Класс не должен реализовывать 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)
  • callable - это вызываемый объект.

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

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

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

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

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

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

PY_VECTORCALL_ARGUMENTS_OFFSET

Если этот флаг установлен в аргументе векторного вызова 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 API vectorcall и связанные с ним функции были временно доступны под именами с ведущим подчеркиванием: _PyObject_Vectorcall, _Py_TPFLAGS_HAVE_VECTORCALL, _PyObject_VectorcallMethod, _PyVectorcall_Function, _PyObject_CallOneArg, _PyObject_CallMethodNoArgs, _PyObject_CallMethodOneArg. Кроме того, PyObject_VectorcallDict был доступен как _PyObject_FastCallDict. Старые имена по-прежнему определяются как псевдонимы новых имен без индексации.

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

При использовании 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)

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

(Py_ssize_t)(nargsf & ~PY_VECTORCALL_ARGUMENTS_OFFSET)

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

Добавлено в версии 3.8.

vectorcallfunc PyVectorcall_Function(PyObject *op)

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

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

Добавлено в версии 3.8.

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

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

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

Добавлено в версии 3.8.

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

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

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

Функция

callable

args

kwargs

PyObject_Call()

PyObject *

кортеж

диктант/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

диктант/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 при неудаче.

Добавлено в версии 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(). Формат может быть NULL, что указывает на отсутствие аргументов.

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

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

Обратите внимание, что если вы передаете только PyObject* args, то PyObject_CallFunctionObjArgs() является более быстрой альтернативой.

Изменено в версии 3.4: Тип формата был изменен с char *.

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

Вызвать метод с именем name объекта obj с переменным количеством аргументов на языке C. Аргументы C описываются строкой формата Py_BuildValue(), которая должна выдать кортеж.

Формат может быть NULL, что указывает на отсутствие аргументов.

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

Это эквивалент выражения Python: obj.name(arg1, arg2, ...).

Обратите внимание, что если вы передаете только PyObject* args, то PyObject_CallMethodObjArgs() является более быстрой альтернативой.

Изменено в версии 3.4: Типы name и format были изменены с 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, где имя метода задано как строковый объект 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)

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

Возвращает результат вызова при успехе, или вызывает исключение и возвращает 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)

Вызов метода с использованием соглашения о вызове 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 при неудаче.

Добавлено в версии 3.9.

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

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

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

Вернуться на верх