Spec-Zone.ru › Python 3.11

Введение

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

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

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

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

Если вы пишете код на 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

Объявляет функцию инициализации модуля расширения PyInit. Тип возвращаемого значения функции — PyObject*. Макрос объявляет все специальные объявления связывания, необходимые для платформы, и для C++ объявляет функцию как extern "C".

Функция инициализации должна иметь имя PyInit_name, где name — имя модуля, и должна быть единственным не-static элементом, определенным в файле модуля. Пример:

static struct PyModuleDef spam_module = {
    PyModuleDef_HEAD_INIT,
    .m_name = "spam",
    ...
};

PyMODINIT_FUNC
PyInit_spam(void)
{
    return PyModule_Create(&spam_module);
}
Py_ABS(x)

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

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

Py_ALWAYS_INLINE

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

Его можно использовать для встраивания критически важных для производительности статических 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_CHARMASK(c)

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

Py_DEPRECATED(version)

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

Пример:

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

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

Py_GETENV(s)

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

Py_MAX(x, y)

Возвращает максимальное значение между x и y.

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

Py_MEMBER_SIZE(type, member)

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

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

Py_MIN(x, y)

Возвращает минимальное значение между x и y.

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

Py_NO_INLINE

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

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

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

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

Py_STRINGIFY(x)

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

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

Py_UNREACHABLE()

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

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

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

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

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

Py_UNUSED(arg)

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

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

PyDoc_STRVAR(name, str)

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

Используйте PyDoc_STRVAR для строк документации, чтобы поддерживать сборку Python без строк документации, как указано в PEP 7.

Пример:

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

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

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

Используйте PyDoc_STR при указании строк документации, чтобы поддерживать сборку Python без строк документации, как указано в PEP 7.

Пример:

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

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

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

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

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

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

Счетчики ссылок всегда манипулируются явно. Обычный способ – использование макроса Py_INCREF() для получения новой ссылки на объект (т. е. увеличение его счетчика ссылок на единицу) и Py_DECREF() для высвобождения этой ссылки (т. е. уменьшение счетчика ссылок на единицу). Макрос Py_DECREF() значительно сложнее, чем инкрементный, поскольку он должен проверить, не становится ли счетчик ссылок нулевым, а затем вызвать деаллокатор объекта. Деаллокатор – это указатель на функцию, содержащийся в структуре типа объекта. Деаллокатор, специфичный для типа, заботится о высвобождении ссылок на другие объекты, содержащиеся в объекте, если это составной тип объекта, например, список, а также выполняет любую дополнительную финализацию, которая требуется. Нет возможности переполнения счетчика ссылок; для хранения счетчика ссылок используется не менее разрядов, чем количество различных местоположений памяти в виртуальной памяти (предполагая 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(), когда она больше не нужна — или передав эту ответственность (обычно своему вызывающему коду). Когда функция передает владение ссылкой своему вызывающему коду, вызывающий код получает новую ссылку. Когда владение не передается, вызывающий код заимствует ссылку. Для заимствованной ссылки ничего делать не нужно.

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

Несколько функций захватывают ссылки; двумя важными исключениями являются 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 проверка ошибок всегда должна быть явной. Все функции в API Python/C могут вызывать исключения, если в документации функции не заявлено обратное. В общем случае, когда функция обнаруживает ошибку, она устанавливает исключение, отбрасывает все имеющиеся у нее ссылки на объекты и возвращает индикатор ошибки. Если не указано иное в документации, этот индикатор равен 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, — это инициализация и, возможно, завершение работы интерпретатора 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 находится в фиксированном месте относительно исполняемого файла интерпретатора Python. В частности, она ищет каталог с именем lib/pythonX.Y относительно родительского каталога, где исполняемый файл с именем python находится в пути поиска командной строки (переменная среды PATH).

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

Приложение, осуществляющее встраивание, может направить поиск, вызвав Py_SetProgramName(file) перед вызовом Py_Initialize(). Обратите внимание, что 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 создаёт то, что обычно понимается как отладочная сборка Python. Py_DEBUG включено в сборке под Unix путём добавления --with-pydebug к команде ./configure. Это также подразумевается наличием неспецифичного для Python макроса _DEBUG. Когда Py_DEBUG включен в сборке под Unix, оптимизация компилятора отключена.

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

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

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

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

Spec-Zone.ru

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