Документация Python неофициальный перевод

extending.md

814 строк · 92.3 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.0/extending/extending.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Расширение Python с помощью C или C++89Добавлять новые встроенные модули в Python довольно просто, если вы умеете программировать на C. Такие *модули расширения* могут делать две вещи, которые невозможно выполнить непосредственно в Python: они могут реализовывать новые встроенные типы объектов и вызывать функции библиотек C и системные вызовы.1011Для поддержки расширений Python API (интерфейс прикладного программирования) определяет набор функций, макросов и переменных, обеспечивающих доступ к большинству аспектов системы времени выполнения Python. Python API включается в C-исходный файл с помощью заголовочного файла `"Python.h"`.1213Компиляция модуля расширения зависит от его предполагаемого использования, а также от настроек вашей системы; подробности приведены в следующих главах.1415## Простой пример1617Давайте создадим модуль расширения с именем `spam` (любимая еда фанатов Monty Python...) и предположим, что мы хотим создать интерфейс Python для функции `system` библиотеки C. [\[1\]](https://python-all.ru/3.0/extending/extending.html#id5) Эта функция принимает строку, завершающуюся нулевым символом, в качестве аргумента и возвращает целое число. Мы хотим, чтобы эту функцию можно было вызывать из Python следующим образом:1819```c20>>> import spam21>>> status = spam.system("ls -l")22```2324Начните с создания файла `spammodule.c`. (Исторически сложилось, что если модуль называется `spam`, то C-файл с его реализацией называется `spammodule.c`; если имя модуля очень длинное, например `spammify`, то файл может называться просто `spammify.c`.)2526Первая строка нашего файла может быть:2728```c29#include <Python.h>30```3132которые подключают Python API (при желании можно добавить комментарий, описывающий назначение модуля, и уведомление об авторских правах).3334> **Предупреждение**35>36> Поскольку Python может определять некоторые макросы препроцессора, влияющие на стандартные заголовки в некоторых системах, необходимо *обязательно* включать `Python.h` до включения любых стандартных заголовков.3738Все видимые пользователю символы, определяемые `Python.h`, имеют префикс `Py` или `PY`, за исключением тех, что определены в стандартных заголовочных файлах. Для удобства, а также поскольку они широко используются интерпретатором Python, `"Python.h"` включает несколько стандартных заголовочных файлов: `<stdio.h>`, `<string.h>`, `<errno.h>` и `<stdlib.h>`. Если последний заголовочный файл отсутствует в вашей системе, он напрямую объявляет функции `malloc`, `free` и `realloc`.3940Следующее, что мы добавим в файл модуля, – это C-функция, которая будет вызвана при вычислении Python-выражения `spam.system(string)` (вскоре мы увидим, как именно она вызывается):4142```c43static PyObject *44spam_system(PyObject *self, PyObject *args)45{46    const char *command;47    int sts;4849    if (!PyArg_ParseTuple(args, "s", &command))50        return NULL;51    sts = system(command);52    return Py_BuildValue("i", sts);53}54```5556Существует прямая трансляция из списка аргументов в Python (например, одиночное выражение `"ls -l"`) в аргументы, передаваемые C-функции. C-функция всегда имеет два аргумента, которые по соглашению называются *self* и *args*.5758Аргумент *self* используется только в том случае, когда C-функция реализует встроенный метод, а не функцию. В примере *self* всегда будет указателем *NULL*, поскольку мы определяем функцию, а не метод. (Это сделано для того, чтобы интерпретатору не приходилось разбираться с двумя разными типами C-функций.)5960Аргумент *args* будет указателем на объект-кортеж Python, содержащий аргументы. Каждый элемент кортежа соответствует аргументу в списке аргументов вызова. Аргументы являются объектами Python – чтобы что-то с ними сделать в нашей C-функции, мы должны преобразовать их в C-значения. Функция [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) в Python API проверяет типы аргументов и преобразует их в C-значения. Она использует строку формата для определения требуемых типов аргументов, а также типов C-переменных, в которые будут сохранены преобразованные значения. Подробнее об этом позже.6162[`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) возвращает true (ненулевое значение), если все аргументы имеют правильный тип и их компоненты были сохранены в переменные, адреса которых переданы. Он возвращает false (нуль), если был передан неверный список аргументов. В последнем случае он также возбуждает соответствующее исключение, чтобы вызывающая функция могла немедленно вернуть *NULL* (как мы видели в примере).6364## Интермеццо: ошибки и исключения6566Важное соглашение во всём интерпретаторе Python заключается в следующем: когда функция завершается неудачей, она должна установить условие исключения и вернуть значение ошибки (обычно указатель *NULL*). Исключения хранятся в статической глобальной переменной внутри интерпретатора; если эта переменная равна *NULL*, исключения не произошло. Вторая глобальная переменная хранит «связанное значение» исключения (второй аргумент [`raise`](https://python-all.ru/3.0/reference/simple_stmts.html#raise)). Третья переменная содержит трассировку стека, если ошибка возникла в Python-коде. Эти три переменные являются C-эквивалентами результата Python-функции [`sys.exc_info()`](https://python-all.ru/3.0/library/sys.html#sys.exc_info) (см. раздел о модуле [`sys`](https://python-all.ru/3.0/library/sys.html#module-sys) в справочнике по библиотеке Python). Важно знать о них, чтобы понимать, как передаются ошибки.6768Python API определяет ряд функций для установки различных типов исключений.6970Наиболее распространённой является [`PyErr_SetString`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_SetString). Её аргументы – объект исключения и C-строка. Объект исключения обычно является предопределённым объектом, таким как `PyExc_ZeroDivisionError`. C-строка указывает причину ошибки и преобразуется в строковый объект Python, который сохраняется как «связанное значение» исключения.7172Ещё одна полезная функция – [`PyErr_SetFromErrno`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_SetFromErrno), которая принимает только аргумент-исключение и строит связанное значение путём проверки глобальной переменной `errno`. Наиболее общей функцией является [`PyErr_SetObject`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_SetObject), которая принимает два объектных аргумента: исключение и его связанное значение. Не нужно выполнять [`Py_INCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_INCREF) для объектов, передаваемых любой из этих функций.7374Можно безопасно проверить, было ли установлено исключение, с помощью [`PyErr_Occurred`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_Occurred). Эта функция возвращает текущий объект исключения или *NULL*, если исключения не было. Обычно не нужно вызывать [`PyErr_Occurred`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_Occurred) для проверки, произошла ли ошибка при вызове функции, поскольку об этом можно судить по возвращаемому значению.7576Когда функция *f*, вызывающая другую функцию *g*, обнаруживает, что последняя завершилась неудачей, *f* сама должна вернуть значение ошибки (обычно *NULL* или `-1`). Она *не* должна вызывать одну из функций `PyErr_*` – одна уже была вызвана функцией *g*. Тогда вызывающий код *f* также должен вернуть признак ошибки своему *вызывающему* коду, опять *не* вызывая `PyErr_*`, и так далее – наиболее подробная причина ошибки уже была сообщена функцией, которая первой её обнаружила. Как только ошибка достигает главного цикла интерпретатора Python, он прерывает выполняемый в данный момент код Python и пытается найти обработчик исключений, указанный программистом на Python.7778(Существуют ситуации, когда модуль может предоставить более подробное сообщение об ошибке, вызвав другую функцию `PyErr_*`, и в таких случаях это допустимо. Однако, как правило, это не требуется и может привести к потере информации о причине ошибки: большинство операций может завершаться неудачей по разным причинам.)7980Чтобы игнорировать исключение, установленное неудачным вызовом функции, состояние исключения необходимо явно очистить вызовом [`PyErr_Clear`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_Clear). Единственный случай, когда C-коду следует вызывать [`PyErr_Clear`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_Clear), – это если он не хочет передавать ошибку интерпретатору, а намерен обработать её полностью самостоятельно (возможно, попробовав что-то другое или сделав вид, что ничего не произошло).8182Каждый неудачный вызов `malloc` должен быть преобразован в исключение – непосредственный вызывающий код `malloc` (или `realloc`) должен вызвать [`PyErr_NoMemory`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_NoMemory) и сам вернуть признак неудачи. Все функции создания объектов (например, [`PyLong_FromLong`](https://python-all.ru/3.0/c-api/long.html#PyLong_FromLong)) уже делают это, поэтому это замечание актуально только для тех, кто вызывает `malloc` напрямую.8384Также обратите внимание, что, за важным исключением [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) и ей подобных, функции, возвращающие целочисленный статус, обычно возвращают положительное значение или ноль при успехе и `-1` при неудаче, как системные вызовы Unix.8586Наконец, не забывайте очищать мусор (вызывая [`Py_XDECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_XDECREF) или [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF) для уже созданных объектов), когда возвращаете признак ошибки!8788Выбор, какое исключение возбуждать, полностью за вами. Существуют предварительно объявленные C-объекты, соответствующие всем встроенным исключениям Python, такие как `PyExc_ZeroDivisionError`, которые можно использовать напрямую. Конечно, следует выбирать исключения с умом – не используйте `PyExc_TypeError` для обозначения того, что файл не удалось открыть (для этого, вероятно, подойдёт `PyExc_IOError`). Если что-то не так со списком аргументов, функция [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) обычно возбуждает `PyExc_TypeError`. Если у вас есть аргумент, значение которого должно находиться в определённом диапазоне или удовлетворять другим условиям, подходит `PyExc_ValueError`.8990Можно также определить новое исключение, уникальное для вашего модуля. Для этого обычно объявляется статическая объектная переменная в начале файла:9192```c93static PyObject *SpamError;94```9596и инициализируйте его в функции инициализации вашего модуля (`PyInit_spam`) объектом исключения (пока опуская проверку ошибок):9798```c99PyMODINIT_FUNC100PyInit_spam(void)101{102    PyObject *m;103104    m = PyModule_Create(&spammodule);105    if (m == NULL)106        return NULL;107108    SpamError = PyErr_NewException("spam.error", NULL, NULL);109    Py_INCREF(SpamError);110    PyModule_AddObject(m, "error", SpamError);111    return m;112}113```114115Обратите внимание, что имя исключения в Python – `spam.error`. Функция [`PyErr_NewException`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_NewException) может создать класс с базовым классом [`Exception`](https://python-all.ru/3.0/library/exceptions.html#exceptions.Exception) (если не передан другой класс вместо *NULL*), описанным в [*Built-in Exceptions*](https://python-all.ru/3.0/library/exceptions.html#bltin-exceptions).116117Также обратите внимание, что переменная `SpamError` сохраняет ссылку на вновь созданный класс исключения; это сделано намеренно! Поскольку исключение может быть удалено из модуля внешним кодом, необходима владеющая ссылка на класс, чтобы гарантировать, что он не будет отброшен, что привело бы к превращению `SpamError` в висячий указатель. Если он станет висячим указателем, C-код, возбуждающий исключение, может вызвать дамп памяти или другие непреднамеренные побочные эффекты.118119Мы обсудим использование PyMODINIT\_FUNC в качестве типа возвращаемого значения функции далее в этом примере.120121## Возвращаясь к примеру122123Вернёмся к нашей функции-примеру. Теперь вы должны понимать этот оператор:124125```c126if (!PyArg_ParseTuple(args, "s", &command))127    return NULL;128```129130Она возвращает *NULL* (признак ошибки для функций, возвращающих указатели на объекты), если в списке аргументов обнаружена ошибка, полагаясь на исключение, установленное [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple). В противном случае строковое значение аргумента копируется в локальную переменную `command`. Это присваивание указателя, и вы не должны изменять строку, на которую он указывает (поэтому в стандартном C переменная `command` должна быть объявлена как `const char *command`).131132Следующий оператор – вызов функции Unix `system` с передачей ей строки, которую мы только что получили от [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple):133134```c135sts = system(command);136```137138Наша функция `spam.system()` должна вернуть значение `sts` как объект Python. Это делается с помощью функции [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue), которая является чем-то вроде обратной к [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple): она принимает строку формата и произвольное количество C-значений и возвращает новый объект Python. Подробнее о [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue) будет рассказано позже.139140```c141return Py_BuildValue("i", sts);142```143144В данном случае она вернёт целочисленный объект. (Да, даже целые числа в Python – это объекты в куче!)145146Если у вас есть C-функция, которая не возвращает полезного аргумента (функция, возвращающая `void`), соответствующая Python-функция должна возвращать `None`. Для этого используется идиома (реализованная макросом [`Py_RETURN_NONE`](https://python-all.ru/3.0/c-api/none.html#Py_RETURN_NONE)):147148```c149Py_INCREF(Py_None);150return Py_None;151```152153[`Py_None`](https://python-all.ru/3.0/c-api/none.html#Py_None) – это C-имя специального Python-объекта `None`. Это полноценный объект Python, а не указатель *NULL*, который, как мы видели, в большинстве контекстов означает «ошибка».154155## Таблица методов модуля и функция инициализации156157Я обещал показать, как `spam_system` вызывается из программ на Python. Сначала нужно перечислить его имя и адрес в «таблице методов»:158159```c160static PyMethodDef SpamMethods[] = {161    ...162    {"system",  spam_system, METH_VARARGS,163     "Execute a shell command."},164    ...165    {NULL, NULL, 0, NULL}        /* Страж */166};167```168169Обратите внимание на третий элемент (`METH_VARARGS`). Это флаг, сообщающий интерпретатору соглашение о вызове, используемое для C-функции. Обычно он всегда должен быть `METH_VARARGS` или `METH_VARARGS | METH_KEYWORDS`; значение `0` означает, что используется устаревший вариант [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple).170171При использовании только `METH_VARARGS` функция должна ожидать, что параметры уровня Python будут переданы в виде кортежа, пригодного для разбора с помощью [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple); дополнительная информация об этой функции приведена ниже.172173Бит [`METH_KEYWORDS`](https://python-all.ru/3.0/c-api/structures.html#METH_KEYWORDS) может быть установлен в третьем поле, если функции должны передаваться именованные аргументы. В этом случае C-функция должна принимать третий параметр `PyObject \*`, который будет словарём именованных аргументов. Используйте [`PyArg_ParseTupleAndKeywords`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTupleAndKeywords) для разбора аргументов такой функции.174175Таблица методов должна быть указана в структуре определения модуля:176177```c178static struct PyModuleDef spammodule = {179   PyModuleDef_HEAD_INIT,180   "spam",   /* имя модуля */181   spam_doc, /* документация модуля, может быть NULL */182   -1,       /* размер состояния модуля для каждого интерпретатора,183                или -1, если модуль хранит состояние в глобальных переменных. */184   SpamMethods185};186```187188Эта структура, в свою очередь, должна быть передана интерпретатору в функции инициализации модуля. Функция инициализации должна называться `PyInit_name`, где *name* – это имя модуля, и она должна быть единственным не-`static` элементом, определённым в файле модуля:189190```c191PyMODINIT_FUNC192PyInit_spam(void)193{194    return PyModule_Create(&spammodule);195}196```197198Обратите внимание, что PyMODINIT\_FUNC объявляет функцию с возвращаемым типом `PyObject *`, объявляет все специальные объявления компоновки, необходимые для платформы, и для C++ объявляет функцию как `extern "C"`.199200Когда программа на Python впервые импортирует модуль `spam`, вызывается `PyInit_spam`. (См. ниже комментарии о встраивании Python.) Она вызывает `PyModule_Create`, которая возвращает объект модуля, и вставляет объекты встроенных функций в только что созданный модуль на основе таблицы (массива структур [`PyMethodDef`](https://python-all.ru/3.0/c-api/structures.html#PyMethodDef)), находящейся в определении модуля. `PyModule_Create` возвращает указатель на создаваемый объект модуля. Она может завершиться фатальной ошибкой при определённых ошибках или вернуть *NULL*, если модуль не удалось инициализировать удовлетворительно. Функция инициализации должна вернуть объект модуля своему вызывающему коду, чтобы он затем был вставлен в `sys.modules`.201202При встраивании Python функция `PyInit_spam` не вызывается автоматически, если только нет записи в таблице `PyImport_Inittab`. Чтобы добавить модуль в таблицу инициализации, используйте [`PyImport_AppendInittab`](https://python-all.ru/3.0/c-api/import.html#PyImport_AppendInittab), за которым опционально следует импорт модуля:203204```c205int206main(int argc, char *argv[])207{208    /* Добавить встроенный модуль перед Py_Initialize */209    PyImport_AppendInittab("spam", PyInit_spam);210211    /* Передать argv[0] интерпретатору Python */212    Py_SetProgramName(argv[0]);213214    /* Инициализировать интерпретатор Python. Обязательно. */215    Py_Initialize();216217    /* Опционально импортировать модуль; в качестве альтернативы,218       импорт может быть отложен до тех пор, пока встроенный скрипт219       не импортирует его. */220    PyImport_ImportModule("spam");221```222223Пример можно найти в файле `Demo/embed/demo.c` в дистрибутиве исходного кода Python.224225> **Примечание**226>227> Удаление записей из `sys.modules` или импорт скомпилированных модулей в несколько интерпретаторов в рамках одного процесса (или после `fork` без последующего `exec`) может создавать проблемы для некоторых модулей расширений. Авторам модулей расширений следует проявлять осторожность при инициализации внутренних структур данных.228229Более существенный пример модуля включён в дистрибутив исходного кода Python как `Modules/xxmodule.c`. Этот файл может использоваться как шаблон или просто прочитан в качестве примера. Скрипт **modulator.py**, входящий в дистрибутив исходного кода или установку Windows, предоставляет простой графический интерфейс для объявления функций и объектов, которые должен реализовать модуль, и может сгенерировать шаблон для заполнения. Скрипт находится в каталоге `Tools/modulator/`; см. файл `README` там для получения дополнительной информации.230231## Компиляция и компоновка232233Прежде чем вы сможете использовать своё новое расширение, нужно сделать ещё две вещи: скомпилировать и скомпоновать его с системой Python. Если вы используете динамическую загрузку, детали могут зависеть от стиля динамической загрузки, применяемого в вашей системе; обратитесь к главам о сборке модулей расширения (глава [*Building C and C++ Extensions with distutils*](https://python-all.ru/3.0/extending/building.html#building)) и дополнительной информации, относящейся только к сборке в Windows (глава [*Building C and C++ Extensions on Windows*](https://python-all.ru/3.0/extending/windows.html#building-on-windows)) для получения дополнительных сведений.234235Если вы не можете использовать динамическую загрузку или хотите сделать свой модуль постоянной частью интерпретатора Python, вам придётся изменить конфигурацию и пересобрать интерпретатор. К счастью, в Unix это очень просто: просто поместите ваш файл (например, `spammodule.c`) в каталог `Modules/` распакованного дистрибутива исходного кода, добавьте строку в файл `Modules/Setup.local`, описывающую ваш файл:236237```c238spam spammodule.o239```240241и пересобрать интерпретатор, выполнив **make** в корневом каталоге. Вы также можете выполнить **make** в подкаталоге `Modules/`, но тогда сначала нужно пересобрать там `Makefile`, выполнив '**make** Makefile'. (Это необходимо делать каждый раз при изменении файла `Setup`.)242243Если ваш модуль требует дополнительных библиотек для компоновки, их также можно перечислить в строке конфигурационного файла, например:244245```c246spam spammodule.o -lX11247```248249## Вызов функций Python из C250251До сих пор мы сосредотачивались на том, как сделать C-функции вызываемыми из Python. Обратное также полезно: вызов функций Python из C. Это особенно актуально для библиотек, поддерживающих так называемые «колбэки». Если C-интерфейс использует колбэки, то эквивалентный Python часто должен предоставить механизм колбэков программисту на Python; реализация потребует вызова функций Python-колбэков из C-колбэка. Можно представить и другие варианты использования.252253К счастью, интерпретатор Python легко вызывается рекурсивно, и существует стандартный интерфейс для вызова функции Python. (Я не буду углубляться в то, как вызвать парсер Python с конкретной строкой в качестве входных данных – если вам интересно, посмотрите на реализацию опции командной строки [*-c*](https://python-all.ru/3.0/using/cmdline.html#cmdoption-c) в `Modules/main.c` из исходного кода Python.)254255Вызов функции Python – это просто. Сначала программа на Python должна каким-то образом передать вам объект функции Python. Вам следует предоставить функцию (или другой интерфейс) для этого. Когда эта функция вызывается, сохраните указатель на объект функции Python (не забудьте сделать [`Py_INCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_INCREF) для него!) в глобальной переменной – или где сочтете нужным. Например, следующая функция может быть частью определения модуля:256257```c258static PyObject *my_callback = NULL;259260static PyObject *261my_set_callback(PyObject *dummy, PyObject *args)262{263    PyObject *result = NULL;264    PyObject *temp;265266    if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {267        if (!PyCallable_Check(temp)) {268            PyErr_SetString(PyExc_TypeError, "parameter must be callable");269            return NULL;270        }271        Py_XINCREF(temp);         /* Добавить ссылку на новый колбэк */272        Py_XDECREF(my_callback);  /* Освободить предыдущий колбэк */273        my_callback = temp;       /* Запомнить новый колбэк */274        /* Шаблон для возврата None */275        Py_INCREF(Py_None);276        result = Py_None;277    }278    return result;279}280```281282Эта функция должна быть зарегистрирована в интерпретаторе с использованием флага [`METH_VARARGS`](https://python-all.ru/3.0/c-api/structures.html#METH_VARARGS); это описано в разделе [*Таблица методов модуля и функция инициализации*](https://python-all.ru/3.0/extending/extending.html#methodtable). Функция [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) и её аргументы описаны в разделе [*Извлечение параметров в функциях расширения*](https://python-all.ru/3.0/extending/extending.html#parsetuple).283284Макросы [`Py_XINCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_XINCREF) и [`Py_XDECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_XDECREF) увеличивают/уменьшают счетчик ссылок объекта и безопасны при наличии указателей *NULL* (но обратите внимание, что *temp* в этом контексте не будет *NULL*). Дополнительная информация о них в разделе [*Счетчики ссылок*](https://python-all.ru/3.0/extending/extending.html#refcounts).285286Позже, когда придет время вызвать функцию, вызывается C-функция `PyEval_CallObject`. У этой функции два аргумента, оба – указатели на произвольные объекты Python: сама функция Python и список аргументов. Список аргументов всегда должен быть объектом-кортежем, длина которого равна числу аргументов. Чтобы вызвать функцию Python без аргументов, передайте NULL или пустой кортеж; чтобы вызвать её с одним аргументом, передайте кортеж из одного элемента. [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue) возвращает кортеж, если его строка формата состоит из нуля или более кодов формата в круглых скобках. Например:287288```c289int arg;290PyObject *arglist;291PyObject *result;292...293arg = 123;294...295/* Вызов колбэка */296arglist = Py_BuildValue("(i)", arg);297result = PyEval_CallObject(my_callback, arglist);298Py_DECREF(arglist);299```300301`PyEval_CallObject` возвращает указатель на объект Python: это возвращаемое значение функции Python. `PyEval_CallObject` является «нейтральным к счетчику ссылок» по отношению к своим аргументам. В примере был создан новый кортеж для списка аргументов, который освобождается [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF) сразу после вызова.302303Возвращаемое значение `PyEval_CallObject` является «новым»: это может быть либо совершенно новый объект, либо существующий объект, чей счетчик ссылок был увеличен. Поэтому, если вы не хотите сохранять его в глобальной переменной, вам следует каким-то образом применить [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF) к результату, даже (особенно!) если вы не заинтересованы в его значении.304305Однако перед этим важно проверить, не является ли возвращаемое значение *NULL*. Если это так, то функция Python завершилась вызовом исключения. Если C-код, который вызвал `PyEval_CallObject`, вызывается из Python, он должен вернуть признак ошибки своему вызывающему коду на Python, чтобы интерпретатор мог напечатать стек вызовов, или вызывающий код на Python мог обработать исключение. Если это невозможно или нежелательно, исключение следует очистить вызовом [`PyErr_Clear`](https://python-all.ru/3.0/c-api/exceptions.html#PyErr_Clear). Например:306307```c308if (result == NULL)309    return NULL; /* Передать ошибку обратно */310...use result...311Py_DECREF(result);312```313314В зависимости от желаемого интерфейса к функции обратного вызова Python, вам также может потребоваться предоставить список аргументов для `PyEval_CallObject`. В некоторых случаях список аргументов также предоставляется программой на Python, через тот же интерфейс, который задал функцию обратного вызова. Затем его можно сохранить и использовать так же, как объект функции. В других случаях вам может потребоваться создать новый кортеж для передачи в качестве списка аргументов. Проще всего это сделать вызовом [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue). Например, если вы хотите передать целочисленный код события, можно использовать следующий код:315316```c317PyObject *arglist;318...319arglist = Py_BuildValue("(l)", eventcode);320result = PyEval_CallObject(my_callback, arglist);321Py_DECREF(arglist);322if (result == NULL)323    return NULL; /* Передать ошибку обратно */324/* Здесь, возможно, используется результат */325Py_DECREF(result);326```327328Обратите внимание на расположение `Py_DECREF(arglist)` сразу после вызова, до проверки ошибок! Также учтите, что строго говоря этот код не полон: [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue) может исчерпать память, и это нужно проверять.329330Вы также можете вызвать функцию с именованными аргументами, используя `PyEval_CallObjectWithKeywords`. Как в примере выше, мы используем [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue) для построения словаря.331332```c333PyObject *dict;334...335dict = Py_BuildValue("{s:i}", "name", val);336result = PyEval_CallObjectWithKeywords(my_callback, NULL, dict);337Py_DECREF(dict);338if (result == NULL)339    return NULL; /* Передать ошибку обратно */340/* Здесь, возможно, используется результат */341Py_DECREF(result);342```343344## Извлечение параметров в функциях расширения345346Функция [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) объявляется следующим образом:347348```c349int PyArg_ParseTuple(PyObject *arg, char *format, ...);350```351352Аргумент *arg* должен быть объектом кортежа, содержащим список аргументов, переданных из Python в C-функцию. Аргумент *format* должен быть строкой формата, чей синтаксис описан в разделе [*Разбор аргументов и построение значений*](https://python-all.ru/3.0/c-api/arg.html#arg-parsing) справочного руководства Python/C API. Остальные аргументы должны быть адресами переменных, чей тип определяется строкой формата.353354Обратите внимание, что хотя [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple) проверяет, что аргументы Python имеют требуемые типы, он не может проверить корректность адресов переменных C, переданных в вызов: если вы допустите там ошибки, ваш код, вероятно, упадет или, по крайней мере, перезапишет случайные биты в памяти. Так что будьте внимательны!355356Обратите внимание, что любые ссылки на объекты Python, передаваемые вызывающему, являются *заимствованными* ссылками; уменьшать их счётчик ссылок не следует.357358Несколько примеров вызовов:359360```c361#define PY_SSIZE_T_CLEAN  /* Сделать так, чтобы "s#" использовало Py_ssize_t, а не int. */362#include <Python.h>363```364365```c366int ok;367int i, j;368long k, l;369const char *s;370Py_ssize_t size;371372ok = PyArg_ParseTuple(args, ""); /* Без аргументов */373    /* Вызов Python: f() */374```375376```c377ok = PyArg_ParseTuple(args, "s", &s); /* Строка */378    /* Возможный вызов Python: f('whoops!') */379```380381```c382ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Два длинных целых и строка */383    /* Возможный вызов Python: f(1, 2, 'three') */384```385386```c387ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);388    /* Пара int и строка, размер которой также возвращается */389    /* Возможный вызов Python: f((1, 2), 'three') */390```391392```c393{394    const char *file;395    const char *mode = "r";396    int bufsize = 0;397    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);398    /* Строка и, опционально, ещё одна строка и целое число */399    /* Возможные вызовы Python:400       f('spam')401       f('spam', 'w')402       f('spam', 'wb', 100000) */403}404```405406```c407{408    int left, top, right, bottom, h, v;409    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",410             &left, &top, &right, &bottom, &h, &v);411    /* Прямоугольник и точка */412    /* Возможный вызов Python:413       f(((0, 0), (400, 300)), (10, 10)) */414}415```416417```c418{419    Py_complex c;420    ok = PyArg_ParseTuple(args, "D:myfunction", &c);421    /* комплексное число, также предоставляющее имя функции для сообщений об ошибках */422    /* Возможный вызов Python: myfunction(1+2j) */423}424```425426## Именованные параметры для функций-расширений427428Функция [`PyArg_ParseTupleAndKeywords`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTupleAndKeywords) объявляется следующим образом:429430```c431int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,432                                char *format, char *kwlist[], ...);433```434435Параметры *arg* и *format* идентичны одноименным параметрам функции [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple). Параметр *kwdict* – это словарь ключевых слов, полученный как третий параметр от среды выполнения Python. Параметр *kwlist* – это заканчивающийся *NULL* список строк, идентифицирующих параметры; имена сопоставляются с информацией о типах из *format* слева направо. В случае успеха [`PyArg_ParseTupleAndKeywords`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTupleAndKeywords) возвращает true, в противном случае возвращает false и вызывает соответствующее исключение.436437> **Примечание**438>439> Вложенные кортежи не могут быть разобраны при использовании именованных аргументов! Переданные именованные параметры, отсутствующие в *kwlist*, приведут к возбуждению исключения [`TypeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.TypeError).440441Вот пример модуля, использующего ключевые слова, основанный на примере Джеффа Филбрика ([philbrick@hks.com](https://python-all.ru/3.0/extending/extending.html)):442443```c444#include "Python.h"445446static PyObject *447keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)448{449    int voltage;450    char *state = "a stiff";451    char *action = "voom";452    char *type = "Norwegian Blue";453454    static char *kwlist[] = {"voltage", "state", "action", "type", NULL};455456    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,457                                     &voltage, &state, &action, &type))458        return NULL;459460    printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",461           action, voltage);462    printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);463464    Py_INCREF(Py_None);465466    return Py_None;467}468469static PyMethodDef keywdarg_methods[] = {470    /* Приведение функции необходимо, поскольку значения PyCFunction471     * принимают только два параметра PyObject*, а keywdarg_parrot() принимает472     * три.473     */474    {"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,475     "Print a lovely skit to standard output."},476    {NULL, NULL, 0, NULL}   /* сторожевое значение */477};478```479480```c481void482initkeywdarg(void)483{484  /* Создать модуль и добавить функции */485  Py_InitModule("keywdarg", keywdarg_methods);486}487```488489## Построение произвольных значений490491Эта функция является аналогом [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple). Она объявляется следующим образом:492493```c494PyObject *Py_BuildValue(char *format, ...);495```496497Она распознает набор единиц формата, аналогичный распознаваемым [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple), но аргументы (которые являются входными данными для функции, а не выходными) должны быть не указателями, а просто значениями. Она возвращает новый объект Python, подходящий для возврата из C-функции, вызываемой из Python.498499Одно отличие от [`PyArg_ParseTuple`](https://python-all.ru/3.0/c-api/arg.html#PyArg_ParseTuple): в то время как последняя требует, чтобы её первый аргумент был кортежем (поскольку списки аргументов Python внутренне всегда представлены кортежами), [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue) не всегда строит кортеж. Она строит кортеж, только если строка формата содержит две или более единиц формата. Если строка формата пуста, она возвращает `None`; если она содержит ровно одну единицу формата, она возвращает объект, описываемый этой единицей формата. Чтобы заставить её вернуть кортеж размера 0 или 1, заключите строку формата в круглые скобки.500501Примеры (слева вызов, справа результирующее значение Python):502503```c504Py_BuildValue("")                        None505Py_BuildValue("i", 123)                  123506Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)507Py_BuildValue("s", "hello")              'hello'508Py_BuildValue("y", "hello")              b'hello'509Py_BuildValue("ss", "hello", "world")    ('hello', 'world')510Py_BuildValue("s#", "hello", 4)          'hell'511Py_BuildValue("y#", "hello", 4)          b'hell'512Py_BuildValue("()")                      ()513Py_BuildValue("(i)", 123)                (123,)514Py_BuildValue("(ii)", 123, 456)          (123, 456)515Py_BuildValue("(i,i)", 123, 456)         (123, 456)516Py_BuildValue("[i,i]", 123, 456)         [123, 456]517Py_BuildValue("{s:i,s:i}",518              "abc", 123, "def", 456)    {'abc': 123, 'def': 456}519Py_BuildValue("((ii)(ii)) (ii)",520              1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))521```522523## Счетчики ссылок524525В таких языках, как C или C++, программист отвечает за динамическое выделение и освобождение памяти в куче. В C это делается с помощью функций `malloc` и `free`. В C++ используются операторы `new` и `delete`, по сути с тем же смыслом, и мы ограничим дальнейшее обсуждение случаем C.526527Каждый блок памяти, выделенный с помощью `malloc`, в конечном итоге должен быть возвращен в пул доступной памяти ровно одним вызовом `free`. Важно вызвать `free` в нужный момент. Если адрес блока забыт, но `free` для него не вызывается, занимаемая им память не может быть повторно использована до завершения программы. Это называется *утечкой памяти*. С другой стороны, если программа вызывает `free` для блока, а затем продолжает его использовать, это создает конфликт с повторным использованием блока через другой вызов `malloc`. Это называется *использованием освобожденной памяти*. Это имеет те же плохие последствия, что и обращение к неинициализированным данным – дампы памяти, неправильные результаты, таинственные сбои.528529Распространённые причины утечек памяти – нестандартные пути выполнения кода. Например, функция может выделить блок памяти, выполнить некоторые вычисления, а затем снова освободить блок. Теперь изменение требований к функции может добавить в вычисление проверку, которая обнаруживает ошибочную ситуацию и может привести к преждевременному возврату из функции. Легко забыть освободить выделенный блок памяти при таком преждевременном выходе, особенно если он был добавлен в код позднее. Такие утечки, будучи однажды внесёнными, часто остаются незамеченными долгое время: ошибочный выход происходит лишь в небольшой доле всех вызовов, а на большинстве современных машин достаточно виртуальной памяти, поэтому утечка становится заметна только в долго работающем процессе, который часто использует функцию с утечкой. Следовательно, важно предотвращать утечки, используя соглашение о кодировании или стратегию, которая минимизирует такого рода ошибки.530531Поскольку Python активно использует `malloc` и `free`, ему нужна стратегия для предотвращения утечек памяти, а также использования освобожденной памяти. Выбранный метод называется *подсчет ссылок*. Принцип прост: каждый объект содержит счетчик, который увеличивается, когда ссылка на объект сохраняется где-либо, и уменьшается, когда ссылка на него удаляется. Когда счетчик достигает нуля, последняя ссылка на объект удалена, и объект освобождается.532533Альтернативная стратегия называется *автоматической сборкой мусора*. (Иногда подсчет ссылок также называют стратегией сборки мусора, поэтому я использую слово «автоматическая», чтобы различать их.) Большое преимущество автоматической сборки мусора в том, что пользователю не нужно явно вызывать `free`. (Другое заявленное преимущество – повышение скорости или эффективности использования памяти, однако это не точно установленный факт.) Недостаток в том, что для C не существует действительно переносимого автоматического сборщика мусора, в то время как подсчет ссылок можно реализовать переносимо (при условии, что доступны функции `malloc` и `free` – что гарантируется стандартом C). Возможно, когда-нибудь для C появится достаточно переносимый автоматический сборщик мусора. А пока приходится мириться с подсчетом ссылок.534535Хотя Python использует традиционную реализацию подсчёта ссылок, он также предлагает детектор циклов, который обнаруживает циклические ссылки. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок; они являются слабым местом сборки мусора, реализованной только с помощью подсчёта ссылок. Циклические ссылки состоят из объектов, которые содержат (возможно, косвенные) ссылки на самих себя, так что каждый объект в цикле имеет ненулевой счётчик ссылок. Типичные реализации подсчёта ссылок не могут освободить память, принадлежащую объектам в циклической ссылке или на которые есть ссылки из объектов в цикле, даже если на сам цикл больше нет ссылок.536537Детектор циклов способен обнаруживать мусорные циклы и может освобождать их при условии, что в Python не реализованы финализаторы (методы [`__del__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__del__)). Если такие финализаторы есть, детектор предоставляет доступ к циклам через модуль [`gc`](https://python-all.ru/3.0/library/gc.html#module-gc) (в частности, переменную `garbage` в этом модуле). Модуль [`gc`](https://python-all.ru/3.0/library/gc.html#module-gc) также предоставляет способ запуска детектора (функция `collect()`), а также интерфейсы конфигурации и возможность отключения детектора во время выполнения. Детектор циклов считается необязательным компонентом; хотя он включён по умолчанию, его можно отключить на этапе сборки с помощью опции *--without-cycle-gc* скрипта **configure** на платформах Unix (включая Mac OS X). Если детектор циклов отключён таким образом, модуль [`gc`](https://python-all.ru/3.0/library/gc.html#module-gc) будет недоступен.538539### Подсчёт ссылок в Python540541Есть два макроса: `Py_INCREF(x)` и `Py_DECREF(x)`, которые обрабатывают увеличение и уменьшение счетчика ссылок. [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF) также освобождает объект, когда счетчик достигает нуля. Для гибкости он не вызывает `free` напрямую – вместо этого он делает вызов через указатель на функцию в *объекте типа* объекта. Для этой цели (и других) каждый объект также содержит указатель на свой объект типа.542543Остается главный вопрос: когда использовать `Py_INCREF(x)` и `Py_DECREF(x)`? Давайте сначала введем некоторые термины. Никто не «владеет» объектом; однако вы можете *владеть ссылкой* на объект. Счетчик ссылок объекта теперь определяется как количество принадлежащих ему ссылок. Владелец ссылки отвечает за вызов [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF), когда ссылка больше не нужна. Владение ссылкой может быть передано. Есть три способа избавиться от принадлежащей ссылки: передать ее, сохранить или вызвать [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF). Забывание избавиться от принадлежащей ссылки приводит к утечке памяти.544545Также можно *заимствовать* [\[2\]](https://python-all.ru/3.0/extending/extending.html#id6) ссылку на объект. Заимствователь ссылки не должен вызывать [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF). Заимствователь не должен удерживать объект дольше, чем владелец, у которого он был заимствован. Использование заимствованной ссылки после того, как владелец от нее отказался, чревато использованием освобожденной памяти, и этого следует полностью избегать. [\[3\]](https://python-all.ru/3.0/extending/extending.html#id7)546547Преимущество заимствования ссылки перед владением в том, что не нужно заботиться о распоряжении ссылкой на всех возможных путях выполнения кода – иными словами, с заимствованной ссылкой нет риска утечки при преждевременном выходе. Недостаток заимствования по сравнению с владением в том, что существуют некоторые тонкие ситуации, когда в, казалось бы, корректном коде заимствованная ссылка может быть использована после того, как владелец, у которого она была заимствована, фактически распорядился ею.548549Заимствованная ссылка может быть преобразована в принадлежащую вызовом [`Py_INCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_INCREF). Это не влияет на статус владельца, у которого была заимствована ссылка – создается новая принадлежащая ссылка, и на нового владельца возлагаются все обязанности владельца (новый владелец должен правильно избавиться от ссылки, равно как и предыдущий владелец).550551### Правила владения552553Всякий раз, когда ссылка на объект передаётся в функцию или из неё, является частью спецификации интерфейса функции, передаётся ли владение вместе со ссылкой или нет.554555Большинство функций, возвращающих ссылку на объект, передают владение вместе со ссылкой. В частности, все функции, предназначение которых – создавать новый объект, такие как [`PyLong_FromLong`](https://python-all.ru/3.0/c-api/long.html#PyLong_FromLong) и [`Py_BuildValue`](https://python-all.ru/3.0/c-api/arg.html#Py_BuildValue), передают владение получателю. Даже если объект на самом деле не новый, вы все равно получаете владение новой ссылкой на этот объект. Например, [`PyLong_FromLong`](https://python-all.ru/3.0/c-api/long.html#PyLong_FromLong) поддерживает кеш популярных значений и может вернуть ссылку на кешированный элемент.556557Многие функции, извлекающие объекты из других объектов, также передают владение вместе со ссылкой, например [`PyObject_GetAttrString`](https://python-all.ru/3.0/c-api/object.html#PyObject_GetAttrString). Однако здесь картина менее ясна, поскольку несколько распространенных функций являются исключениями: [`PyTuple_GetItem`](https://python-all.ru/3.0/c-api/tuple.html#PyTuple_GetItem), [`PyList_GetItem`](https://python-all.ru/3.0/c-api/list.html#PyList_GetItem), [`PyDict_GetItem`](https://python-all.ru/3.0/c-api/dict.html#PyDict_GetItem) и [`PyDict_GetItemString`](https://python-all.ru/3.0/c-api/dict.html#PyDict_GetItemString) – все возвращают ссылки, которые вы заимствуете из кортежа, списка или словаря.558559Функция [`PyImport_AddModule`](https://python-all.ru/3.0/c-api/import.html#PyImport_AddModule) также возвращает заимствованную ссылку, хотя она может фактически создать возвращаемый объект: это возможно, потому что принадлежащая ссылка на объект хранится в `sys.modules`.560561Когда вы передаете ссылку на объект в другую функцию, как правило, функция заимствует у вас ссылку – если ей нужно сохранить ее, она использует [`Py_INCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_INCREF), чтобы стать независимым владельцем. Есть ровно два важных исключения из этого правила: [`PyTuple_SetItem`](https://python-all.ru/3.0/c-api/tuple.html#PyTuple_SetItem) и [`PyList_SetItem`](https://python-all.ru/3.0/c-api/list.html#PyList_SetItem). Эти функции принимают владение переданным им элементом – даже в случае неудачи! (Обратите внимание, что [`PyDict_SetItem`](https://python-all.ru/3.0/c-api/dict.html#PyDict_SetItem) и подобные не принимают владение – они «нормальные».)562563Когда из Python вызывается C-функция, она заимствует ссылки на свои аргументы у вызывающего кода. Вызывающий код владеет ссылкой на объект, поэтому время жизни заимствованной ссылки гарантировано до возврата из функции. Только когда такую заимствованную ссылку необходимо сохранить или передать дальше, её нужно превратить в собственную ссылку, вызвав [`Py_INCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_INCREF).564565Ссылка на объект, возвращаемая из C-функции, вызванной из Python, должна быть собственной – владение передаётся от функции её вызывающей стороне.566567### Тонкий лёд568569Есть несколько ситуаций, когда безобидное, на первый взгляд, использование заимствованной ссылки может привести к проблемам. Все они связаны с неявными вызовами интерпретатора, которые могут заставить владельца ссылки освободить её.570571Первый и самый важный случай, о котором нужно знать – это использование [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF) на постороннем объекте во время заимствования ссылки на элемент списка. Например:572573```c574void575bug(PyObject *list)576{577    PyObject *item = PyList_GetItem(list, 0);578579    PyList_SetItem(list, 1, PyLong_FromLong(0L));580    PyObject_Print(item, stdout, 0); /* ОШИБКА! */581}582```583584Эта функция сначала заимствует ссылку на `list[0]`, затем заменяет `list[1]` значением `0` и наконец печатает заимствованную ссылку. Выглядит безобидно, не так ли? Но это не так!585586Проследим поток управления внутри [`PyList_SetItem`](https://python-all.ru/3.0/c-api/list.html#PyList_SetItem). Список владеет ссылками на все свои элементы, поэтому при замене элемента 1 ему необходимо избавиться от исходного элемента 1. Теперь предположим, что исходный элемент 1 был экземпляром пользовательского класса, и что этот класс определяет метод [`__del__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__del__). Если счетчик ссылок этого экземпляра класса равен 1, его удаление вызовет метод [`__del__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__del__).587588Поскольку он написан на Python, метод [`__del__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__del__) может выполнять произвольный код на Python. Может ли он сделать что-то, что приведёт к недействительности ссылки на `item` в `bug`? Ещё как! Предполагая, что список, переданный в `bug`, доступен методу [`__del__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__del__), он может выполнить инструкцию вроде `del list[0]`. И если это была последняя ссылка на этот объект, память, связанная с ним, будет освобождена, тем самым сделав недействительным `item`.589590Решение, если знать источник проблемы, простое: временно увеличить счётчик ссылок. Правильная версия функции выглядит так:591592```c593void594no_bug(PyObject *list)595{596    PyObject *item = PyList_GetItem(list, 0);597598    Py_INCREF(item);599    PyList_SetItem(list, 1, PyLong_FromLong(0L));600    PyObject_Print(item, stdout, 0);601    Py_DECREF(item);602}603```604605Это реальная история. В старой версии Python были варианты этой ошибки, и кто-то потратил значительное время в отладчике C, чтобы выяснить, почему его методы [`__del__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__del__) не срабатывали...606607Вторая проблема с заимствованной ссылкой – это вариант, связанный с потоками. Обычно несколько потоков в интерпретаторе Python не мешают друг другу, потому что существует глобальная блокировка, защищающая всё пространство объектов Python. Однако можно временно освободить эту блокировку с помощью макроса [`Py_BEGIN_ALLOW_THREADS`](https://python-all.ru/3.0/c-api/init.html#Py_BEGIN_ALLOW_THREADS), а затем снова захватить её с помощью [`Py_END_ALLOW_THREADS`](https://python-all.ru/3.0/c-api/init.html#Py_END_ALLOW_THREADS). Это обычно делается вокруг блокирующих операций ввода-вывода, чтобы другие потоки могли использовать процессор, пока ожидается завершение ввода-вывода. Очевидно, следующая функция имеет ту же проблему, что и предыдущая:608609```c610void611bug(PyObject *list)612{613    PyObject *item = PyList_GetItem(list, 0);614    Py_BEGIN_ALLOW_THREADS615    ...some blocking I/O call...616    Py_END_ALLOW_THREADS617    PyObject_Print(item, stdout, 0); /* ОШИБКА! */618}619```620621### Нулевые указатели622623В общем, функции, принимающие ссылки на объекты в качестве аргументов, не ожидают, что им будут передавать указатели *NULL*, и при такой передаче может произойти аварийное завершение (дамп ядра) или возникнуть последующие сбои. Функции, возвращающие ссылки на объекты, обычно возвращают *NULL* только для указания на то, что произошло исключение. Причина, по которой не проверяются аргументы на *NULL*, заключается в том, что функции часто передают полученные объекты другим функциям – если бы каждая функция проверяла *NULL*, было бы много избыточных проверок и код работал бы медленнее.624625Лучше проверять на *NULL* только «у источника»: когда получен указатель, который может быть *NULL*, например, из `malloc` или из функции, которая может возбуждать исключение.626627Макросы [`Py_INCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_INCREF) и [`Py_DECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_DECREF) не проверяют указатели на *NULL* – однако их варианты [`Py_XINCREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_XINCREF) и [`Py_XDECREF`](https://python-all.ru/3.0/c-api/refcounting.html#Py_XDECREF) делают это.628629Макросы для проверки конкретного типа объекта (`Pytype_Check()`) не проверяют *NULL*-указатели – опять же, существует много кода, который вызывает несколько таких макросов подряд, чтобы проверить объект на соответствие различным ожидаемым типам, и это привело бы к избыточным проверкам. Не существует вариантов с проверкой *NULL*.630631Механизм вызова C-функций гарантирует, что список аргументов, передаваемый C-функциям (`args` в примерах), никогда не является *NULL* – на самом деле он гарантирует, что это всегда кортеж. [\[4\]](https://python-all.ru/3.0/extending/extending.html#id8)632633Серьёзной ошибкой является допустить, чтобы указатель *NULL* «попал» к пользователю Python.634635## Написание расширений на C++636637Можно писать модули расширения на C++. Существуют некоторые ограничения. Если главная программа (интерпретатор Python) компилируется и компонуется C-компилятором, глобальные или статические объекты с конструкторами использоваться не могут. Это не проблема, если главная программа компонуется C++-компилятором. Функции, которые будут вызываться интерпретатором Python (в частности, функции инициализации модулей), должны быть объявлены с использованием `extern "C"`. Нет необходимости заключать заголовочные файлы Python в `extern "C" {...}` – они уже используют эту форму, если определён символ `__cplusplus` (все современные C++-компиляторы определяют этот символ).638639## Предоставление C API для модуля расширения640641Многие модули расширения просто предоставляют новые функции и типы для использования из Python, но иногда код в модуле расширения может быть полезен для других модулей расширения. Например, модуль расширения может реализовать тип «коллекция», который работает как список без порядка. Подобно тому, как стандартный тип списка Python имеет C API, позволяющий модулям расширения создавать списки и управлять ими, этот новый тип коллекции должен иметь набор C-функций для прямого управления из других модулей расширения.642643На первый взгляд это кажется простым: просто написать функции (не объявляя их `static`, конечно), предоставить соответствующий заголовочный файл и задокументировать C API. И на самом деле это сработало бы, если бы все модули расширения всегда были статически скомпонованы с интерпретатором Python. Однако когда модули используются как разделяемые библиотеки, символы, определённые в одном модуле, могут быть невидимы для другого модуля. Детали видимости зависят от операционной системы; некоторые системы используют одно глобальное пространство имён для интерпретатора Python и всех модулей расширения (например, Windows), тогда как другие требуют явного списка импортируемых символов на этапе компоновки модуля (например, AIX) или предлагают выбор различных стратегий (большинство Unices). И даже если символы глобально видимы, модуль, функции которого требуется вызвать, возможно, ещё не загружен!644645Таким образом, переносимость требует не делать никаких предположений о видимости символов. Это означает, что все символы в модулях расширения должны быть объявлены как `static`, за исключением функции инициализации модуля, чтобы избежать конфликтов имён с другими модулями расширения (как обсуждалось в разделе [*The Module’s Method Table and Initialization Function*](https://python-all.ru/3.0/extending/extending.html#methodtable)). И это означает, что символы, которые *должны* быть доступны из других модулей расширения, должны экспортироваться иным способом.646647Python предоставляет специальный механизм для передачи информации уровня C (указателей) из одного модуля расширения в другой: CObjects. CObject – это тип данных Python, который хранит указатель (`void *`). CObjects можно создавать и использовать только через их C API, но их можно передавать как любой другой объект Python. В частности, их можно присвоить имени в пространстве имён модуля расширения. Другие модули расширения могут затем импортировать этот модуль, получить значение этого имени и извлечь указатель из CObject.648649Существует много способов использования CObjects для экспорта C API модуля расширения. Каждому имени можно выделить собственный CObject, или все указатели C API можно хранить в массиве, адрес которого публикуется в CObject. А различные задачи по сохранению и извлечению указателей могут распределяться разными способами между модулем, предоставляющим код, и клиентскими модулями.650651Следующий пример демонстрирует подход, который возлагает основную нагрузку на разработчика экспортирующего модуля, что уместно для часто используемых библиотечных модулей. Он хранит все указатели C API (в примере только один!) в массиве указателей `void`, который становится значением CObject. Заголовочный файл, соответствующий модулю, предоставляет макрос, который берёт на себя импорт модуля и получение его указателей C API; клиентским модулям нужно лишь вызвать этот макрос перед обращением к C API.652653Экспортирующий модуль является модификацией модуля `spam` из раздела [*Простой пример*](https://python-all.ru/3.0/extending/extending.html#extending-simpleexample). Функция `spam.system()` вызывает не напрямую библиотечную функцию C `system`, а функцию `PySpam_System`, которая, конечно, делала бы в реальности что-то более сложное (например, добавляла бы «spam» к каждой команде). Эта функция `PySpam_System` также экспортируется в другие модули расширения.654655Функция `PySpam_System` – это обычная C-функция, объявленная как `static`, как и всё остальное:656657```c658static int659PySpam_System(const char *command)660{661    return system(command);662}663```664665Функция `spam_system` тривиальным образом изменена:666667```c668static PyObject *669spam_system(PyObject *self, PyObject *args)670{671    const char *command;672    int sts;673674    if (!PyArg_ParseTuple(args, "s", &command))675        return NULL;676    sts = PySpam_System(command);677    return Py_BuildValue("i", sts);678}679```680681В начале модуля, сразу после строки682683```c684#include "Python.h"685```686687необходимо добавить ещё две строки:688689```c690#define SPAM_MODULE691#include "spammodule.h"692```693694`#define` используется, чтобы сообщить заголовочному файлу, что он включается в экспортирующий модуль, а не в клиентский. Наконец, функция инициализации модуля должна позаботиться об инициализации массива указателей C API:695696```c697PyMODINIT_FUNC698PyInit_spam(void)699{700    PyObject *m;701    static void *PySpam_API[PySpam_API_pointers];702    PyObject *c_api_object;703704    m = PyModule_Create(&spammodule);705    if (m == NULL)706        return NULL;707708    /* Инициализация массива указателей C API */709    PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;710711    /* Создать CObject, содержащий адрес массива указателей API */712    c_api_object = PyCObject_FromVoidPtr((void *)PySpam_API, NULL);713714    if (c_api_object != NULL)715        PyModule_AddObject(m, "_C_API", c_api_object);716    return m;717}718```719720Обратите внимание, что `PySpam_API` объявлен `static`; иначе массив указателей исчезнет при завершении `PyInit_spam()`!721722Основная часть работы выполняется в заголовочном файле `spammodule.h`, который выглядит следующим образом:723724```c725#ifndef Py_SPAMMODULE_H726#define Py_SPAMMODULE_H727#ifdef __cplusplus728extern "C" {729#endif730731/* Заголовочный файл для spammodule */732733/* Функции C API */734#define PySpam_System_NUM 0735#define PySpam_System_RETURN int736#define PySpam_System_PROTO (const char *command)737738/* Общее количество указателей C API */739#define PySpam_API_pointers 1740741#ifdef SPAM_MODULE742/* Этот раздел используется при компиляции spammodule.c */743744static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;745746#else747/* Этот раздел используется в модулях, которые используют API spammodule */748749static void **PySpam_API;750751#define PySpam_System \752 (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])753754/* Возвращает -1 и устанавливает исключение при ошибке, 0 при успехе. */755static int756import_spam(void)757{758    PyObject *module = PyImport_ImportModule("spam");759760    if (module != NULL) {761        PyObject *c_api_object = PyObject_GetAttrString(module, "_C_API");762        if (c_api_object == NULL)763            return -1;764        if (PyCObject_Check(c_api_object))765            PySpam_API = (void **)PyCObject_AsVoidPtr(c_api_object);766        Py_DECREF(c_api_object);767    }768    return 0;769}770771#endif772773#ifdef __cplusplus774}775#endif776777#endif /* !defined(Py_SPAMMODULE_H) */778```779780Всё, что должен сделать клиентский модуль для получения доступа к функции `PySpam_System` – это вызвать функцию (вернее, макрос) `import_spam` в своей функции инициализации:781782```c783PyMODINIT_FUNC784initclient(void)785{786    PyObject *m;787788    m = Py_InitModule("client", ClientMethods);789    if (m == NULL)790        return;791    if (import_spam() < 0)792        return;793    /* дополнительная инициализация может быть выполнена здесь */794}795```796797Основной недостаток этого подхода в том, что файл `spammodule.h` является довольно сложным. Однако базовая структура одинакова для каждой экспортируемой функции, поэтому её нужно освоить лишь один раз.798799Наконец, стоит упомянуть, что CObjects предоставляют дополнительную функциональность, которая особенно полезна для выделения и освобождения памяти указателя, хранящегося в CObject. Подробности описаны в Python/C API Reference Manual в разделе [*CObjects*](https://python-all.ru/3.0/c-api/cobject.html#cobjects) и в реализации CObjects (файлы `Include/cobject.h` и `Objects/cobject.c` в дистрибутиве исходного кода Python).800801Сноски802803| [\[1\]](https://python-all.ru/3.0/extending/extending.html#id1) | Интерфейс для этой функции уже существует в стандартном модуле [`os`](https://python-all.ru/3.0/library/os.html#module-os) – он был выбран как простой и понятный пример. |804| --- | --- |805806| [\[2\]](https://python-all.ru/3.0/extending/extending.html#id2) | Метафора «заимствования» ссылки не совсем точна: владелец всё ещё имеет копию ссылки. |807| --- | --- |808809| [\[3\]](https://python-all.ru/3.0/extending/extending.html#id3) | Проверка того, что счётчик ссылок не меньше 1, **не работает** – сам счётчик ссылок может находиться в освобождённой памяти и поэтому может быть повторно использован для другого объекта! |810| --- | --- |811812| [\[4\]](https://python-all.ru/3.0/extending/extending.html#id4) | Эти гарантии не действуют при использовании «старого» стиля соглашения о вызовах – он всё ещё встречается в большом объёме существующего кода. |813| --- | --- |814