Spec-Zone.ru › Python 3.9

Введение

Интерфейс программиста для Python предоставляет программистам на C и C++ доступ к интерпретатору Python на различных уровнях. Интерфейс одинаково удобен для использования из 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, а версия — '%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}
};

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

Большинство функций API Python/C имеют один или несколько аргументов, а также возвращаемое значение типа 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;
}

Типы

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

Py_ssize_t

Целочисленный тип со знаком, такой что 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, который будет выполняться позже, её нужно явно установить с помощью вызова 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, отключение оптимизации компилятора.

В дополнение к отладке ссылок, описанной ниже, выполняются следующие дополнительные проверки:

  • Добавлены дополнительные проверки в механизм выделения объектов.
  • Добавлены дополнительные проверки в анализатор и компилятор.
  • Проверки перевода с расширенных типов на узкие типы проверяются на потерю информации.
  • Добавлен ряд утверждений в реализации словарей и множеств. Кроме того, объект множества получает метод test_c_api().
  • Добавлены проверки целостности входных аргументов при создании фрейма.
  • Память для целых чисел инициализируется известным недопустимым шаблоном для обнаружения ссылок на неинициализированные цифры.
  • Добавлена низкоуровневая трассировка и дополнительные проверки исключений в виртуальной машине выполнения.
  • Добавлены дополнительные проверки в реализацию области памяти.
  • Добавлена дополнительная отладка в модуль потоков.

Возможно, существуют дополнительные проверки, не упомянутые здесь.

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

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

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

Spec-Zone.ru

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