Spec-Zone.ru › Python 3.14

Введение

Интерфейс прикладного программирования Python предоставляет программистам на C и C++ доступ к интерпретатору Python на различных уровнях. API также можно использовать из C++, но для краткости его обычно называют Python/C API. Существует две принципиально разные причины использовать Python/C API. Первая — написание модулей расширения для конкретных целей; это модули на C, расширяющие интерпретатор Python. Вероятно, это наиболее распространённый способ использования. Вторая причина — использование Python в качестве компонента более крупного приложения; этот метод обычно называют встраиванием Python в приложение.

Написание модуля расширения — относительно хорошо изученный процесс, для которого хорошо подходит подход «по кулинарной книге». Существуют различные инструменты, в некоторой степени автоматизирующие этот процесс. Python встраивают в другие приложения с первых лет его существования, однако процесс встраивания Python менее прост, чем написание расширения.

Многие функции API полезны независимо от того, встраиваете вы Python или расширяете его; кроме того, большинству приложений со встроенным Python потребуется предоставить пользовательское расширение, поэтому, вероятно, стоит освоить написание расширений, прежде чем пытаться встроить Python в реальное приложение.

Совместимость с версиями языков

C API Python совместим с версиями C и C++, соответствующими стандартам C11 и C++11.

Это нижняя граница: C API не требует возможностей более поздних версий C/C++. Вам не нужно включать «режим c11» компилятора.

Стандарты кодирования

Если вы пишете код на C для включения в CPython, вы обязаны следовать рекомендациям и стандартам, определённым в PEP 7. Эти рекомендации применяются независимо от версии Python, в которую вы вносите вклад. Соблюдать эти соглашения необязательно для собственных сторонних модулей расширения, если только вы не планируете в дальнейшем внести их в Python.

Файлы заголовков

Все определения функций, типов и макросов, необходимые для использования Python/C API, включаются в код следующей строкой:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

Это подразумевает включение следующих стандартных заголовков: <stdio.h>, <string.h>, <errno.h>, <limits.h>, <assert.h> и <stdlib.h> (если доступны).

Примечание

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

Рекомендуется всегда определять PY_SSIZE_T_CLEAN перед включением Python.h. Описание этого макроса см. в разделе Разбор аргументов и создание значений.

Все видимые пользователю имена, определённые в Python.h (за исключением имён, определённых во включённых стандартных заголовках), имеют префикс Py или _Py. Имена, начинающиеся с _Py, предназначены для внутреннего использования реализацией Python и не должны использоваться авторами расширений. Имена членов структур не имеют зарезервированного префикса.

Примечание

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

Файлы заголовков обычно устанавливаются вместе с Python. В Unix они расположены в каталогах prefix/include/pythonversion/ и exec_prefix/include/pythonversion/, где prefix и exec_prefix задаются соответствующими параметрами скрипта configure Python, а version — это '%d.%d' % sys.version_info[:2]. В Windows заголовки устанавливаются в prefix/include, где prefix — каталог установки, указанный в программе установки.

Чтобы включить заголовки, добавьте оба каталога (если они различаются) в путь поиска подключаемых файлов компилятора. Не добавляйте в путь поиска родительские каталоги, а затем не используйте #include <pythonX.Y/Python.h>; это приведёт к ошибкам при сборке для нескольких платформ, поскольку независимые от платформы заголовки в prefix включают заголовки, специфичные для платформы, из exec_prefix.

Пользователям C++ следует учитывать, что, хотя API полностью определён с использованием C, в файлах заголовков точки входа правильно объявлены как extern "C". Поэтому для использования API из C++ не требуется никаких специальных действий.

Полезные макросы

В файлах заголовков Python определено несколько полезных макросов. Многие из них определены рядом с местом, где используются (например, Py_RETURN_NONE, PyMODINIT_FUNC). Другие макросы общего назначения определены здесь. Этот список не обязательно является полным.

Py_CAN_START_THREADS

Если этот макрос определён, текущая система может запускать потоки.

В настоящее время все системы, поддерживаемые CPython (согласно PEP 11), за исключением некоторых платформ WebAssembly, поддерживают запуск потоков.

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

Py_GETENV(s)

Аналог getenv(s), но возвращает NULL, если в командной строке был передан параметр -E (см. PyConfig.use_environment).

Макросы для строк документации

PyDoc_STRVAR(name, str)

Создаёт переменную с именем name, которую можно использовать в строках документации. Если Python собран без строк документации (--without-doc-strings), значением будет пустая строка.

Пример:

PyDoc_STRVAR(pop_doc, "Remove and return the rightmost element.");

static PyMethodDef deque_methods[] = {
    // ...
    {"pop", (PyCFunction)deque_pop, METH_NOARGS, pop_doc},
    // ...
}

Разворачивается в PyDoc_VAR(name) = PyDoc_STR(str).

PyDoc_STR(str)

Разворачивается в заданную входную строку или в пустую строку, если строки документации отключены (--without-doc-strings).

Пример:

static PyMethodDef pysqlite_row_methods[] = {
    {"keys", (PyCFunction)pysqlite_row_keys, METH_NOARGS,
        PyDoc_STR("Returns the keys of the row.")},
    {NULL, NULL}
};
PyDoc_VAR(name)

Объявляет статический символьный массив с заданным именем name. Разворачивается в static const char name[]

Например:

PyDoc_VAR(python_doc) = PyDoc_STR(
   "A genus of constricting snakes in the Pythonidae family native "
   "to the tropics and subtropics of the Eastern Hemisphere.");

Макросы общего назначения

Следующие макросы предназначены для распространённых задач, не специфичных для Python.

Py_UNUSED(arg)

Используйте этот макрос для неиспользуемых аргументов в определении функции, чтобы подавить предупреждения компилятора. Пример: int func(int a, int Py_UNUSED(b)) { return a; }.

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

Py_GCC_ATTRIBUTE(name)

Используйте атрибут GCC с именем name, скрывая его от компиляторов, не поддерживающих атрибуты GCC (например, MSVC).

В компиляторе GCC он разворачивается в __attribute__((name)), а в компиляторах, не поддерживающих атрибуты GCC, ни во что не разворачивается.

Средства для работы с числами

Py_ABS(x)

Возвращает абсолютное значение x.

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

Если результат невозможно представить (например, если x имеет значение INT_MIN для типа int), поведение не определено.

Приблизительно соответствует ((x) < 0 ? -(x) : (x))

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

Py_MAX(x, y)
Py_MIN(x, y)

Возвращают соответственно большее или меньшее из аргументов.

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

Py_MAX приблизительно соответствует (((x) > (y)) ? (x) : (y)).

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

Py_ARITHMETIC_RIGHT_SHIFT(type, integer, positions)

Аналог integer >> positions, но принудительно выполняет расширение знака, поскольку стандарт C не определяет, будет ли при сдвиге вправо знакового целого числа выполняться расширение знака или заполнение нулями.

integer должен иметь любой знаковый целочисленный тип. positions — количество позиций для сдвига вправо.

И integer, и positions могут вычисляться несколько раз; поэтому не передавайте этому макросу напрямую вызов функции или другую операцию с побочными эффектами. Вместо этого сохраните результат в переменной и передайте её.

type не используется и сохранён только для обратной совместимости. Ранее type использовался для приведения integer.

Изменено в версии 3.1: Теперь этот макрос допустим для всех знаковых целочисленных типов, а не только для тех, для которых допустим unsigned type. В результате type больше не используется.

Py_CHARMASK(c)

Аргумент должен быть символом или целым числом в диапазоне [-128, 127] или [0, 255]. Этот макрос возвращает c, приведённое к типу unsigned char.

Средства проверки утверждений

Py_UNREACHABLE()

Используйте этот макрос для пути выполнения, который по замыслу недостижим. Например, в ветви default: оператора switch, если все возможные значения охвачены операторами case. Используйте его в местах, где может возникнуть соблазн поместить вызов assert(0) или abort().

В режиме выпуска макрос помогает компилятору оптимизировать код и позволяет избежать предупреждения о недостижимом коде. Например, в режиме выпуска для GCC макрос реализован с помощью __builtin_unreachable().

В режиме отладки и в неподдерживаемых компиляторах макрос разворачивается в вызов Py_FatalError().

Макрос Py_UNREACHABLE() можно использовать после вызова функции, которая никогда не возвращает управление, но не объявлена как _Noreturn.

Этот макрос нельзя использовать для маловероятного пути выполнения, который может быть достигнут в исключительных случаях. Например, при нехватке памяти или если системный вызов возвращает значение вне ожидаемого диапазона. В таком случае лучше сообщить об ошибке вызывающему коду. Если сообщить об ошибке вызывающему коду невозможно, можно использовать Py_FatalError().

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

Py_SAFE_DOWNCAST(value, larger, smaller)

Приводит value от типа larger к типу smaller, проверяя, что информация не потеряна.

В выпусках Python для сборки это приблизительно эквивалентно ((smaller) value) (в C++ вместо этого используется static_cast<smaller>(value)).

В отладочных сборках (то есть когда определён Py_DEBUG) выполняется проверка, что при приведении от larger к smaller информация не потеряна.

value, larger и smaller могут вычисляться несколько раз в выражении; поэтому не передавайте этому макросу напрямую выражение с побочными эффектами.

Py_BUILD_ASSERT(cond)

Проверяет условие времени компиляции cond в виде оператора. Сборка завершится неудачей, если условие ложно или его невозможно вычислить во время компиляции.

Приблизительно соответствует static_assert(cond) в C23 и более поздних версиях.

Например:

Py_BUILD_ASSERT(sizeof(PyTime_t) == sizeof(int64_t));

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

Py_BUILD_ASSERT_EXPR(cond)

Проверяет условие времени компиляции cond в виде выражения, результатом которого является 0. Сборка завершится неудачей, если условие ложно или его невозможно вычислить во время компиляции.

Например:

#define foo_to_char(foo) \
    ((char *)(foo) + Py_BUILD_ASSERT_EXPR(offsetof(struct foo, string) == 0))

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

Средства для работы с размерами типов

Py_ARRAY_LENGTH(array)

Вычисляет во время компиляции длину статически выделенного массива C.

Аргумент array должен быть массивом C, размер которого известен во время компиляции. Передача массива с неизвестным размером, например массива, выделенного в куче, приведёт к ошибке компиляции в некоторых компиляторах или к неверным результатам в остальных.

Приблизительно эквивалентно следующему:

sizeof(array) / sizeof((array)[0])
Py_MEMBER_SIZE(type, member)

Возвращает размер члена member структуры (type) в байтах.

Приблизительно соответствует sizeof(((type *)NULL)->member).

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

Средства для определения макросов

Py_FORCE_EXPANSION(X)

Эквивалентно X; полезно для склеивания токенов в макросах, поскольку препроцессор принудительно вычисляет раскрытия макросов в X.

Py_STRINGIFY(x)

Преобразует x в строку C. Например, Py_STRINGIFY(123) возвращает "123".

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

Средства объявления

Следующие макросы можно использовать в объявлениях. Они особенно полезны для определения самого C API и имеют ограниченное применение для авторов расширений. Большинство из них разворачиваются в специфичные для компилятора обозначения распространённых расширений языка C.

Py_ALWAYS_INLINE

Просит компилятор всегда встраивать статическую inline-функцию. Компилятор может проигнорировать это указание и решить не встраивать функцию.

Соответствует атрибуту always_inline в GCC и __forceinline в MSVC.

Его можно использовать для встраивания критичных к производительности статических inline-функций при сборке Python в режиме отладки с отключённым встраиванием функций. Например, MSC отключает встраивание функций при сборке в режиме отладки.

Бездумная пометка статической inline-функции макросом Py_ALWAYS_INLINE может ухудшить производительность (например, из-за увеличения размера кода). Обычно компилятор лучше разработчика оценивает соотношение затрат и преимуществ.

Если Python собран в режиме отладки (если определён макрос Py_DEBUG), макрос Py_ALWAYS_INLINE ничего не делает.

Его необходимо указывать перед типом возвращаемого функцией значения. Использование:

static inline Py_ALWAYS_INLINE int random(void) { return 4; }

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

Py_NO_INLINE

Отключает встраивание функции. Например, это уменьшает использование стека C; полезно в сборках LTO+PGO, интенсивно встраивающих код (см. bpo-33720).

Соответствует атрибуту/спецификатору noinline в GCC и MSVC.

Использование:

Py_NO_INLINE static int random(void) { return 4; }

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

Py_DEPRECATED(version)

Используйте этот макрос для объявления API, признанных устаревшими в определённой версии CPython. Макрос необходимо разместить перед именем символа.

Пример:

Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);

Изменено в версии 3.8: Добавлена поддержка MSVC.

Py_LOCAL(type)

Объявляет функцию, возвращающую указанный type, с квалификатором быстрого вызова для функций, локальных для текущего файла. Семантически это эквивалентно static type.

Py_LOCAL_INLINE(type)

Эквивалентен Py_LOCAL, но дополнительно запрашивает встраивание функции.

Py_LOCAL_SYMBOL

Макрос для объявления символа локальным для разделяемой библиотеки (скрытым). На поддерживаемых платформах он гарантирует, что символ не будет экспортирован.

В совместимых версиях GCC/Clang он разворачивается в __attribute__((visibility("hidden"))).

Py_EXPORTED_SYMBOL

Макрос для объявления символа (функции или данных) экспортируемым. В Windows он разворачивается в __declspec(dllexport). В совместимых версиях GCC/Clang он разворачивается в __attribute__((visibility("default"))). Этот макрос предназначен для определения самого C API; модулям расширения не следует его использовать.

Py_IMPORTED_SYMBOL

Макрос для объявления символа импортируемым. В Windows он разворачивается в __declspec(dllimport). Этот макрос предназначен для определения самого C API; модулям расширения не следует его использовать.

PyAPI_FUNC(type)

Макрос, используемый CPython для объявления функции частью C API. Его раскрытие зависит от платформы и конфигурации сборки. Этот макрос предназначен для определения самого C API CPython; модулям расширения не следует использовать его для собственных символов.

PyAPI_DATA(type)

Макрос, используемый CPython для объявления общедоступной глобальной переменной частью C API. Его раскрытие зависит от платформы и конфигурации сборки. Этот макрос предназначен для определения самого C API CPython; модулям расширения не следует использовать его для собственных символов.

Устаревшие макросы

Следующие макросы использовались для возможностей, стандартизированных в C11.

Py_ALIGNED(num)

Задаёт выравнивание по границе num байт в компиляторах, которые это поддерживают.

Рассмотрите возможность использования вместо этого стандартного спецификатора C11 _Alignas.

Py_LL(number)
Py_ULL(number)

Используют number соответственно как целочисленный литерал типа long long или unsigned long long.

Разворачиваются в number с суффиксом LL или LLU соответственно, но в некоторых старых компиляторах разворачиваются в специфичные для компилятора суффиксы.

Рассмотрите возможность непосредственного использования суффиксов стандартов C99: LL и LLU.

Py_MEMCPY(dest, src, n)

Это псевдоним memcpy().

Не рекомендуется, но пока поддерживается начиная с версии 3.14: Вместо этого используйте непосредственно memcpy().

Py_VA_COPY

Это псевдоним функции va_copy из стандарта C99.

Ранее для копирования va_list использовался специфичный для компилятора метод.

Изменено в версии 3.6: Теперь это псевдоним va_copy.

Не рекомендуется, но пока поддерживается начиная с версии 3.14.

Объекты, типы и счетчики ссылок

Большинство функций Python/C API имеют один или несколько аргументов, а также возвращаемое значение типа PyObject*. Этот тип представляет собой указатель на непрозрачный тип данных, представляющий произвольный объект Python. Поскольку в большинстве ситуаций язык Python одинаково обрабатывает все типы объектов Python (например, при присваивании, применении правил области видимости и передаче аргументов), вполне естественно, что они представлены одним типом C. Почти все объекты Python находятся в куче: вы никогда не объявляете автоматическую или статическую переменную типа PyObject, можно объявлять только переменные-указатели типа PyObject*. Единственное исключение — объекты типов; поскольку их нельзя освобождать, обычно это статические объекты PyTypeObject.

У всех объектов Python (даже у целых чисел Python) есть тип и счетчик ссылок. Тип объекта определяет, что это за объект (например, целое число, список или пользовательская функция; существует множество других типов, описанных в разделе Стандартная иерархия типов). Для каждого из известных типов существует макрос, проверяющий, относится ли объект к этому типу; например, PyList_Check(a) имеет значение true тогда и только тогда, когда объект, на который указывает a, является списком Python.

Счетчики ссылок

Счетчик ссылок важен, поскольку объем памяти современных компьютеров конечен (и часто весьма ограничен); он подсчитывает количество мест, из которых на объект ведет сильная ссылка. Таким местом может быть другой объект, глобальная (или статическая) переменная C либо локальная переменная какой-либо функции C. Когда освобождается последняя сильная ссылка на объект (то есть его счетчик ссылок становится равен нулю), объект освобождается. Если он содержит ссылки на другие объекты, эти ссылки освобождаются. Эти другие объекты, в свою очередь, могут быть освобождены, если на них больше нет ссылок, и так далее. (Здесь очевидна проблема с объектами, ссылающимися друг на друга; пока решение простое: «не делайте так».)

Счетчики ссылок всегда изменяются явно. Обычно для получения новой ссылки на объект (то есть увеличения его счетчика ссылок на единицу) используют макрос Py_INCREF(), а для освобождения этой ссылки (то есть уменьшения счетчика ссылок на единицу) — Py_DECREF(). Макрос Py_DECREF() значительно сложнее макроса incref, поскольку он должен проверить, стал ли счетчик ссылок равен нулю, и в таком случае вызвать функцию освобождения объекта. Указатель на эту функцию находится в структуре типа объекта. Функция освобождения конкретного типа отвечает за освобождение ссылок на другие объекты, содержащиеся в объекте, если это составной тип объекта, например список, а также выполняет необходимую дополнительную финализацию. Переполнение счетчика ссылок невозможно: для хранения счетчика используется как минимум столько же битов, сколько существует различных ячеек памяти в виртуальной памяти (при условии sizeof(Py_ssize_t) >= sizeof(void*)). Поэтому увеличение счетчика ссылок — простая операция.

Необязательно удерживать сильную ссылку (то есть увеличивать счетчик ссылок) для каждой локальной переменной, содержащей указатель на объект. В теории счетчик ссылок объекта увеличивается на единицу, когда переменная начинает на него указывать, и уменьшается на единицу, когда переменная выходит из области видимости. Однако эти изменения взаимно компенсируются, и в итоге значение счетчика ссылок не меняется. Единственная реальная причина использовать счетчик ссылок — не допустить освобождения объекта, пока на него указывает наша переменная. Если мы знаем, что существует по крайней мере одна другая ссылка на объект, которая останется действительной не меньше времени, чем наша переменная, временно получать новую сильную ссылку (то есть увеличивать счетчик ссылок) не нужно. Важный случай, когда это актуально, — объекты, передаваемые в качестве аргументов функциям C из Python в модуле расширения: механизм вызова гарантирует, что на каждый аргумент будет удерживаться ссылка на протяжении всего вызова.

Однако распространенная ошибка — извлечь объект из списка и какое-то время хранить его, не получая новую ссылку. Какая-либо другая операция может удалить объект из списка, освободив эту ссылку и, возможно, сам объект. Реальная опасность заключается в том, что безобидные на вид операции могут вызвать произвольный код Python, который сделает это; существует путь выполнения, позволяющий передать управление обратно пользователю из Py_DECREF(), поэтому потенциально опасной может оказаться почти любая операция.

Надежный подход — всегда использовать универсальные операции (функции, имена которых начинаются с PyObject_, PyNumber_, PySequence_ или PyMapping_). Эти операции всегда создают новую сильную ссылку (то есть увеличивают счетчик ссылок) на возвращаемый объект. В этом случае вызывающая сторона должна вызвать Py_DECREF(), когда результат больше не нужен; это быстро входит в привычку.

Подробности о счетчиках ссылок

Поведение функций Python/C API в отношении счетчиков ссылок лучше всего объяснять через владение ссылками. Владение относится к ссылкам, а не к объектам (объекты никому не принадлежат: они всегда являются общими). «Владеть ссылкой» означает отвечать за вызов Py_DECREF, когда ссылка больше не нужна. Владение также можно передать: тогда код, получивший право владения ссылкой, отвечает за ее освобождение вызовом Py_DECREF() или Py_XDECREF(), когда она больше не нужна, либо за передачу этой ответственности дальше (обычно вызывающей стороне). Если функция передает владение ссылкой вызывающей стороне, говорят, что вызывающая сторона получает новую ссылку. Если владение не передается, говорят, что вызывающая сторона заимствует ссылку. Для заимствованной ссылки ничего делать не нужно.

И наоборот, когда вызывающая функция передает ссылку на объект, возможны два варианта: функция забирает ссылку на объект либо не забирает ее.

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

Лишь немногие функции забирают ссылки; два примечательных исключения — PyList_SetItem() и PyTuple_SetItem(), которые забирают ссылку на элемент (но не на кортеж или список, в который этот элемент помещается!). Эти функции были созданы с возможностью забирать ссылку из-за распространенного способа заполнения кортежа или списка вновь созданными объектами; например, код для создания кортежа (1, 2, "three") мог бы выглядеть так (пока не учитываем обработку ошибок; более правильный способ написания кода показан ниже):

PyObject *t;

t = PyTuple_New(3);
PyTuple_SetItem(t, 0, PyLong_FromLong(1L));
PyTuple_SetItem(t, 1, PyLong_FromLong(2L));
PyTuple_SetItem(t, 2, PyUnicode_FromString("three"));

Здесь PyLong_FromLong() возвращает новую ссылку, которую сразу забирает PyTuple_SetItem(). Если вы хотите продолжать использовать объект, несмотря на то что ссылка на него будет забрана, перед вызовом функции, забирающей ссылку, используйте Py_INCREF(), чтобы получить еще одну ссылку.

Кстати, PyTuple_SetItem() — единственный способ задать элементы кортежа; PySequence_SetItem() и PyObject_SetItem() отказываются это делать, поскольку кортежи — неизменяемый тип данных. Используйте PyTuple_SetItem() только для кортежей, которые создаете сами.

Аналогичный код для заполнения списка можно написать с помощью PyList_New() и PyList_SetItem().

Однако на практике вы редко будете использовать эти способы создания и заполнения кортежа или списка. Существует универсальная функция Py_BuildValue(), которая может создавать большинство распространенных объектов из значений C, руководствуясь строкой формата. Например, приведенные выше два блока кода можно заменить следующим кодом (который также обрабатывает ошибки):

PyObject *tuple, *list;

tuple = Py_BuildValue("(iis)", 1, 2, "three");
list = Py_BuildValue("[iis]", 1, 2, "three");

Гораздо чаще PyObject_SetItem() и подобные функции используют с элементами, ссылки на которые вы только заимствуете, например с аргументами, переданными в написанную вами функцию. В этом случае поведение таких функций в отношении ссылок гораздо разумнее: вам не нужно получать новую ссылку только для того, чтобы передать ее дальше («позволить забрать»). Например, эта функция устанавливает для всех элементов списка (а точнее, любой изменяемой последовательности) заданный элемент:

int
set_all(PyObject *target, PyObject *item)
{
    Py_ssize_t i, n;

    n = PyObject_Length(target);
    if (n < 0)
        return -1;
    for (i = 0; i < n; i++) {
        PyObject *index = PyLong_FromSsize_t(i);
        if (!index)
            return -1;
        if (PyObject_SetItem(target, index, item) < 0) {
            Py_DECREF(index);
            return -1;
        }
        Py_DECREF(index);
    }
    return 0;
}

С возвращаемыми функциями значениями ситуация немного иная. Передача ссылки большинству функций не меняет ваших обязанностей по владению этой ссылкой, однако многие функции, возвращающие ссылку на объект, передают вам владение этой ссылкой. Причина проста: во многих случаях возвращаемый объект создается непосредственно при вызове, и полученная вами ссылка — единственная ссылка на этот объект. Поэтому универсальные функции, возвращающие ссылки на объекты, например PyObject_GetItem() и PySequence_GetItem(), всегда возвращают новую ссылку (вызывающая сторона становится владельцем ссылки).

Важно понимать, что владение ссылкой, возвращаемой функцией, зависит только от того, какую функцию вы вызываете, — оперение (тип объекта, переданного функции в качестве аргумента) здесь ни при чем! Поэтому, если извлечь элемент из списка с помощью PyList_GetItem(), вы не будете владеть ссылкой, а если получить тот же элемент из того же списка с помощью PySequence_GetItem() (которая принимает точно такие же аргументы), вы будете владеть ссылкой на возвращаемый объект.

Ниже показано, как можно написать функцию, вычисляющую сумму элементов списка целых чисел: сначала с помощью PyList_GetItem(), а затем с помощью PySequence_GetItem().

long
sum_list(PyObject *list)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject *item;

    n = PyList_Size(list);
    if (n < 0)
        return -1; /* Not a list */
    for (i = 0; i < n; i++) {
        item = PyList_GetItem(list, i); /* Can't fail */
        if (!PyLong_Check(item)) continue; /* Skip non-integers */
        value = PyLong_AsLong(item);
        if (value == -1 && PyErr_Occurred())
            /* Integer too big to fit in a C long, bail out */
            return -1;
        total += value;
    }
    return total;
}
long
sum_sequence(PyObject *sequence)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject *item;
    n = PySequence_Length(sequence);
    if (n < 0)
        return -1; /* Has no length */
    for (i = 0; i < n; i++) {
        item = PySequence_GetItem(sequence, i);
        if (item == NULL)
            return -1; /* Not a sequence, or other failure */
        if (PyLong_Check(item)) {
            value = PyLong_AsLong(item);
            Py_DECREF(item);
            if (value == -1 && PyErr_Occurred())
                /* Integer too big to fit in a C long, bail out */
                return -1;
            total += value;
        }
        else {
            Py_DECREF(item); /* Discard reference ownership */
        }
    }
    return total;
}

Типы

В Python/C API есть еще несколько типов данных, играющих важную роль; большинство из них — простые типы C, такие как int, long, double и char*. Несколько типов структур используются для описания статических таблиц, перечисляющих функции, экспортируемые модулем, или атрибуты данных нового типа объектов; еще одна структура описывает значение комплексного числа. Они будут рассмотрены вместе с функциями, которые их используют.

type Py_ssize_t
Часть стабильного ABI.

Знаковый целочисленный тип, такой что sizeof(Py_ssize_t) == sizeof(size_t). В C99 такой тип напрямую не определен (size_t — беззнаковый целочисленный тип). Подробности см. в PEP 353. PY_SSIZE_T_MAX — наибольшее положительное значение типа Py_ssize_t.

Исключения

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

Однако программистам на C всегда необходимо явно проверять наличие ошибок. Все функции Python/C API могут возбуждать исключения, если в документации конкретной функции явно не указано иное. Как правило, при возникновении ошибки функция устанавливает исключение, освобождает все принадлежащие ей ссылки на объекты и возвращает индикатор ошибки. Если не указано иное, этот индикатор — либо NULL, либо -1, в зависимости от типа возвращаемого функцией значения. Некоторые функции возвращают логический результат true/false, где false указывает на ошибку. Очень немногие функции не возвращают явный индикатор ошибки или имеют неоднозначное возвращаемое значение; для проверки ошибки в них требуется явно вызвать PyErr_Occurred(). Эти исключения всегда явно описаны в документации.

Состояние исключения хранится в потоке (в однопоточной программе это эквивалентно хранению в глобальной области). Поток может находиться в одном из двух состояний: исключение произошло или не произошло. Для проверки этого можно использовать функцию PyErr_Occurred(): если исключение произошло, она возвращает заимствованную ссылку на объект типа исключения, а в противном случае — NULL. Существует несколько функций для установки состояния исключения: PyErr_SetString() — наиболее распространенная (хотя и не самая универсальная) функция для установки состояния исключения, а PyErr_Clear() очищает его.

Полное состояние исключения состоит из трех объектов (каждый из которых может быть NULL): типа исключения, соответствующего значения исключения и трассировки стека. Их значения совпадают со значениями результата Python для sys.exc_info(), однако это не одно и то же: объекты Python представляют последнее исключение, обрабатываемое оператором Python try … except, тогда как состояние исключения на уровне C существует только во время передачи исключения между функциями C, пока оно не достигнет главного цикла интерпретатора байт-кода Python, который отвечает за передачу его в sys.exc_info() и подобные объекты.

Обратите внимание, что начиная с Python 1.5 предпочтительным и потокобезопасным способом доступа к состоянию исключения из кода Python является вызов функции sys.exc_info(), возвращающей состояние исключения текущего потока для кода Python. Кроме того, семантика обоих способов доступа к состоянию исключения изменилась: функция, перехватывающая исключение, сохраняет и восстанавливает состояние исключения своего потока, чтобы сохранить состояние исключения вызывающей функции. Это предотвращает распространенные ошибки в коде обработки исключений, вызванные тем, что безобидно выглядящая функция перезаписывает обрабатываемое исключение; кроме того, это уменьшает часто нежелательное продление времени жизни объектов, на которые ссылаются кадры стека в трассировке.

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

Простой пример обнаружения исключений и их передачи приведен в описанном выше примере sum_sequence(). В этом примере при обнаружении ошибки не требуется очищать принадлежащие функции ссылки. В следующем примере функции показана очистка после ошибки. Сначала, чтобы напомнить вам, почему вам нравится Python, приведем эквивалентный код на Python:

def incr_item(dict, key):
    try:
        item = dict[key]
    except KeyError:
        item = 0
    dict[key] = item + 1

А вот соответствующий код на C во всей красе:

int
incr_item(PyObject *dict, PyObject *key)
{
    /* Objects all initialized to NULL for Py_XDECREF */
    PyObject *item = NULL, *const_one = NULL, *incremented_item = NULL;
    int rv = -1; /* Return value initialized to -1 (failure) */

    item = PyObject_GetItem(dict, key);
    if (item == NULL) {
        /* Handle KeyError only: */
        if (!PyErr_ExceptionMatches(PyExc_KeyError))
            goto error;

        /* Clear the error and use zero: */
        PyErr_Clear();
        item = PyLong_FromLong(0L);
        if (item == NULL)
            goto error;
    }
    const_one = PyLong_FromLong(1L);
    if (const_one == NULL)
        goto error;

    incremented_item = PyNumber_Add(item, const_one);
    if (incremented_item == NULL)
        goto error;

    if (PyObject_SetItem(dict, key, incremented_item) < 0)
        goto error;
    rv = 0; /* Success */
    /* Continue with cleanup code */

 error:
    /* Cleanup code, shared by success and failure path */

    /* Use Py_XDECREF() to ignore NULL references */
    Py_XDECREF(item);
    Py_XDECREF(const_one);
    Py_XDECREF(incremented_item);

    return rv; /* -1 for error, 0 for success */
}

Этот пример демонстрирует одобренный способ использования оператора goto в C! В нем показано применение PyErr_ExceptionMatches() и PyErr_Clear() для обработки определенных исключений, а также Py_XDECREF() для освобождения принадлежащих функции ссылок, которые могут быть NULL (обратите внимание на 'X' в имени; Py_DECREF() привел бы к сбою при обработке ссылки со значением NULL). Для правильной работы важно инициализировать переменные, используемые для хранения принадлежащих функции ссылок, значением NULL; аналогично предполагаемое возвращаемое значение инициализируется значением -1 (ошибка) и получает значение успеха только после успешного выполнения последнего вызова.

Встраивание Python

Единственная важная задача, о которой должны заботиться только разработчики встраиваемых приложений (в отличие от авторов расширений) для интерпретатора Python, — инициализация и, возможно, завершение работы интерпретатора. Большинство функций интерпретатора можно использовать только после его инициализации.

Основная функция инициализации — Py_Initialize(). Она инициализирует таблицу загруженных модулей и создает основные модули builtins, __main__ и sys. Она также инициализирует путь поиска модулей (sys.path).

Py_Initialize() не задает «список аргументов скрипта» (sys.argv). Если эта переменная нужна коду Python, который будет выполнен позднее, необходимо задать PyConfig.argv и PyConfig.parse_argv: см. раздел Конфигурация инициализации Python.

В большинстве систем (в частности, в Unix и Windows, хотя детали немного различаются) функция Py_Initialize() вычисляет путь поиска модулей, исходя из предположения о местонахождении стандартного исполняемого файла интерпретатора Python и фиксированном расположении библиотеки Python относительно него. В частности, она ищет каталог с именем lib/pythonX.Y относительно родительского каталога, в котором в пути поиска команд оболочки найден исполняемый файл с именем python (переменная окружения PATH).

Например, если исполняемый файл Python найден в /usr/local/bin/python, предполагается, что библиотеки находятся в /usr/local/lib/pythonX.Y. (Фактически этот конкретный путь также является резервным расположением, используемым, если в PATH не найден исполняемый файл с именем python.) Пользователь может переопределить это поведение, задав переменную окружения PYTHONHOME, или добавить каталоги перед стандартным путем, задав PYTHONPATH.

Встраиваемое приложение может управлять поиском, задав PyConfig.program_name до вызова Py_InitializeFromConfig(). Обратите внимание, что PYTHONHOME по-прежнему переопределяет это значение, а PYTHONPATH по-прежнему добавляется перед стандартным путем. Приложение, которому необходим полный контроль, должно предоставить собственную реализацию функций Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() и Py_GetProgramFullPath() (все они определены в Modules/getpath.c).

Иногда требуется «деинициализировать» Python. Например, приложению может понадобиться начать работу заново (повторно вызвать Py_Initialize()) или оно больше не использует Python и хочет освободить память, выделенную Python. Для этого можно вызвать Py_FinalizeEx(). Функция Py_IsInitialized() возвращает true, если Python в данный момент инициализирован. Более подробная информация об этих функциях приведена в следующей главе. Обратите внимание, что Py_FinalizeEx() не освобождает всю память, выделенную интерпретатором Python; например, память, выделенная модулями расширения, пока освободить невозможно.

Отладочные сборки

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

Полный список различных типов отладочных сборок приведен в файле Misc/SpecialBuilds.txt из дистрибутива исходного кода Python. Доступны сборки, поддерживающие трассировку счетчиков ссылок, отладку распределителя памяти и низкоуровневое профилирование основного цикла интерпретатора. В оставшейся части этого раздела описаны только наиболее часто используемые сборки.

Py_DEBUG

Компиляция интерпретатора с определенным макросом Py_DEBUG создает то, что обычно называют отладочной сборкой Python. В сборке для Unix Py_DEBUG включается добавлением --with-pydebug к команде ./configure. Его наличие также подразумевается при наличии не специфичного для Python макроса _DEBUG. Если в сборке для Unix включен Py_DEBUG, оптимизация компилятора отключается.

Помимо описанной ниже отладки счетчиков ссылок выполняются дополнительные проверки; см. раздел Отладочная сборка Python.

Определение Py_TRACE_REFS включает трассировку ссылок (см. параметр configure --with-trace-refs option). При его определении для активных объектов поддерживается циклический двусвязный список: в каждый PyObject добавляются два дополнительных поля. Также отслеживается общее количество выделений памяти. При выходе печатаются все существующие ссылки. (В интерактивном режиме это происходит после каждого оператора, выполненного интерпретатором.)

Более подробную информацию см. в Misc/SpecialBuilds.txt из дистрибутива исходного кода Python.

Рекомендуемые сторонние инструменты

Следующие сторонние инструменты предлагают как простые, так и более сложные способы создания расширений на C, C++ и Rust для Python:

  • Cython
  • cffi
  • HPy
  • nanobind (C++)
  • Numba
  • pybind11 (C++)
  • PyO3 (Rust)
  • SWIG

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

Эти проекты не поддерживаются теми же людьми, которые сопровождают Python, поэтому о проблемах следует сообщать непосредственно в соответствующие проекты. Не забывайте проверять, что проект по-прежнему сопровождается и поддерживается, поскольку приведенный выше список может устареть.

См. также

Руководство пользователя по упаковке Python: бинарные расширения

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

© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/c-api/intro.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API