Spec-Zone.ru › Python 3.10

Введение

Интерфейс программиста для 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 определяются соответствующими параметрами скрипта настройки 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). Другие, более универсальные, определены здесь. Это не обязательно полный список.

Py_UNREACHABLE()

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

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

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

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

Новое в версии 3.7.

Py_ABS(x)

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

Новое в версии 3.3.

Py_MIN(x, y)

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

Новое в версии 3.3.

Py_MAX(x, y)

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

Новое в версии 3.3.

Py_STRINGIFY(x)

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

Новое в версии 3.4.

Py_MEMBER_SIZE(type, member)

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

Новое в версии 3.6.

Py_CHARMASK(c)

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

Py_GETENV(s)

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

Py_UNUSED(arg)

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

Новое в версии 3.4.

Py_DEPRECATED(version)

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

Пример:

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

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

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}
};
END_OF_DOCUMENT_MARKER

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

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

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

Небольшое количество функций захватывают ссылки; двумя заметными исключениями являются 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 (в отличие от авторов расширений), является инициализация и, возможно, завершение работы интерпретатора Python. Большая часть функциональности интерпретатора доступна только после его инициализации.

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

Py_Initialize() не устанавливает список аргументов сценария (sys.argv). Если этот параметр нужен коду Python, который будет выполняться позже, его необходимо явно установить с помощью вызова PySys_SetArgvEx(argc, argv, updatepath) после вызова Py_Initialize().

На большинстве систем (в частности, на 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.10/c-api/intro.html

Spec-Zone.ru

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