Введение
Интерфейс программирования приложений (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 -
Просит компилятор всегда встраивать статическую встроенную функцию. Компилятор может игнорировать это и решить не встраивать функцию.
Может использоваться для встраивания критически важных для производительности статических встроенных функций при построении Python в отладочном режиме с отключением встраивания функций. Например, MSC отключает встраивание функций при построении в отладочном режиме.
Слепое использование макроса 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был передан в командной строке (см.PyConfig.use_environment).
-
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.
Приложение для встраивания может направить поиск, установив PyConfig.program_name перед вызовом Py_InitializeFromConfig(). Обратите внимание, что PYTHONHOME всё равно переопределяет это, и PYTHONPATH всё равно вставляется перед стандартным путём. Приложение, которое требует полного контроля, должно предоставить собственное реализацию Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() и Py_GetProgramFullPath() (все определены в Modules/getpath.c).
Иногда желательно «разынициализировать» Python. Например, приложение может захотеть начать сначала (вызвать Py_Initialize() ещё раз) или приложение просто закончит использование Python и хочет освободить выделенную память Python. Это можно сделать, вызвав Py_FinalizeEx(). Функция Py_IsInitialized() возвращает true, если Python находится в инициализированном состоянии. Более подробную информацию об этих функциях можно найти в следующей главе. Обратите внимание, что Py_FinalizeEx() не освобождает всю память, выделенную интерпретатором Python, например, память, выделенная модулями расширений, в настоящее время не может быть освобождена.
Сборки отладки
Python можно скомпилировать с несколькими макросами, чтобы включить дополнительные проверки интерпретатора и модулей расширений. Эти проверки обычно добавляют большое количество накладных расходов во время выполнения, поэтому они по умолчанию не включены.
Полный список различных типов сборок отладки находится в файле Misc/SpecialBuilds.txt в дистрибутиве исходного кода Python. Доступны сборки, поддерживающие отслеживание ссылок, отладку выделения памяти или низкоуровневое профилирование основного цикла интерпретатора. В оставшейся части этого раздела будут описаны только самые часто используемые сборки.
-
Py_DEBUG
Компиляция интерпретатора с определённым макросом Py_DEBUG производит то, что обычно понимается как сборка отладки Python. 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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/c-api/intro.html