extending.md
1> **Источник:** https://python-all.ru/3.5/extending/extending.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 1. Расширение Python с помощью C или C++89Добавлять новые встроенные модули в Python довольно просто, если вы умеете программировать на C. Такие *модули расширения* могут делать две вещи, которые невозможно выполнить непосредственно в Python: они могут реализовывать новые встроенные типы объектов и вызывать функции библиотек C и системные вызовы.1011Для поддержки расширений Python API (интерфейс прикладного программирования) определяет набор функций, макросов и переменных, предоставляющих доступ к большинству аспектов системы времени выполнения Python. Python API включается в C-файл с помощью заголовка `"Python.h"`.1213Компиляция модуля расширения зависит от его предполагаемого использования, а также от настроек вашей системы; подробности приведены в следующих главах.1415> **Примечание**16>17> Интерфейс C-расширений специфичен для CPython, и модули расширений не работают в других реализациях Python. Во многих случаях можно обойтись без написания C-расширений и сохранить переносимость на другие реализации. Например, если ваш вариант использования – вызов функций библиотек C или системных вызовов, стоит рассмотреть использование модуля [`ctypes`](https://python-all.ru/3.5/library/ctypes.html#module-ctypes) или библиотеки [cffi](https://python-all.ru/3.5/extending/extending.html) вместо написания собственного кода на C. Эти модули позволяют писать код на Python для взаимодействия с кодом на C и являются более переносимыми между реализациями Python, чем написание и компиляция модуля расширения на C.1819## 1.1. Простой пример2021Давайте создадим модуль расширения с именем `spam` (любимая еда фанатов Монти Пайтона…) и предположим, что мы хотим создать интерфейс Python для функции библиотеки C `system()`. [\[1\]](https://python-all.ru/3.5/extending/extending.html#id5) Эта функция принимает в качестве аргумента строку символов, завершающуюся нулевым символом, и возвращает целое число. Мы хотим, чтобы эту функцию можно было вызывать из Python следующим образом:2223```c24>>> import spam25>>> status = spam.system("ls -l")26```2728Начните с создания файла `spammodule.c`. (Исторически сложилось, что если модуль называется `spam`, то C-файл, содержащий его реализацию, называется `spammodule.c`; если имя модуля очень длинное, например `spammify`, то имя модуля может быть просто `spammify.c`.)2930Первая строка нашего файла может быть:3132```c33#include <Python.h>34```3536которые подключают Python API (при желании можно добавить комментарий, описывающий назначение модуля, и уведомление об авторских правах).3738> **Примечание**39>40> Поскольку Python может определять некоторые директивы препроцессора, которые влияют на стандартные заголовочные файлы в некоторых системах, *необходимо* включать `Python.h` до того, как будут включены любые стандартные заголовочные файлы.4142Все видимые пользователю символы, определённые `Python.h`, имеют префикс `Py` или `PY`, за исключением определённых в стандартных заголовочных файлах. Для удобства, а также поскольку они широко используются интерпретатором Python, `"Python.h"` включает несколько стандартных заголовочных файлов: `<stdio.h>`, `<string.h>`, `<errno.h>` и `<stdlib.h>`. Если последнего заголовочного файла нет в вашей системе, в нём напрямую объявляются функции `malloc()`, `free()` и `realloc()`.4344Следующее, что мы добавим в наш файл модуля, – это C-функция, которая будет вызываться при вычислении выражения Python `spam.system(string)` (чуть позже мы увидим, как именно она в итоге вызывается):4546```c47static PyObject *48spam_system(PyObject *self, PyObject *args)49{50 const char *command;51 int sts;5253 if (!PyArg_ParseTuple(args, "s", &command))54 return NULL;55 sts = system(command);56 return PyLong_FromLong(sts);57}58```5960Существует прямой способ отображения списка аргументов в Python (например, одиночного выражения `"ls -l"`) на аргументы, передаваемые C-функции. C-функция всегда имеет два аргумента, традиционно называемых *self* и *args*.6162Аргумент *self* указывает на объект модуля для функций уровня модуля; для метода он указывал бы на экземпляр объекта.6364Аргумент *args* будет указателем на кортеж Python, содержащий аргументы. Каждый элемент кортежа соответствует одному аргументу из списка аргументов вызова. Аргументы – это объекты Python; чтобы выполнить с ними какие-либо действия в нашей C-функции, их необходимо преобразовать в значения C. Функция [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) из Python API проверяет типы аргументов и преобразует их в значения C. Она использует строку формата для определения требуемых типов аргументов, а также типов переменных C, в которые будут сохранены преобразованные значения. Подробнее об этом позже.6566[`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) возвращает true (ненулевое значение), если все аргументы имеют правильный тип и их компоненты сохранены в переменные, адреса которых были переданы. Она возвращает false (ноль), если передан недопустимый список аргументов. В последнем случае она также возбуждает соответствующее исключение, чтобы вызывающая функция могла немедленно вернуть *NULL* (как мы видели в примере).6768## 1.2. Интермеццо: Ошибки и исключения6970Важное соглашение во всем интерпретаторе Python заключается в следующем: когда функция терпит неудачу, она должна установить условие исключения и вернуть значение ошибки (обычно указатель *NULL*). Исключения хранятся в статической глобальной переменной внутри интерпретатора; если эта переменная равна *NULL*, исключения не произошло. Вторая глобальная переменная хранит «связанное значение» исключения (второй аргумент [`raise`](https://python-all.ru/3.5/reference/simple_stmts.html#raise)). Третья переменная содержит стек вызовов (traceback) на случай, если ошибка возникла в коде Python. Эти три переменные являются C-эквивалентами результата [`sys.exc_info()`](https://python-all.ru/3.5/library/sys.html#sys.exc_info) в Python (см. раздел о модуле [`sys`](https://python-all.ru/3.5/library/sys.html#module-sys) в справочнике по библиотеке Python). Важно знать о них, чтобы понимать, как передаются ошибки.7172Python API определяет ряд функций для установки различных типов исключений.7374Самая распространённая из них – [`PyErr_SetString()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_SetString). Её аргументы – объект исключения и C-строка. Объект исключения обычно является предопределённым объектом, таким как `PyExc_ZeroDivisionError`. C-строка указывает причину ошибки и преобразуется в строковый объект Python, который сохраняется как «связанное значение» исключения.7576Другая полезная функция – [`PyErr_SetFromErrno()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_SetFromErrno), которая принимает только аргумент-исключение и формирует связанное значение, проверяя глобальную переменную `errno`. Самая общая функция – [`PyErr_SetObject()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_SetObject), принимающая два объектных аргумента: исключение и его связанное значение. Не требуется [`Py_INCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_INCREF) объекты, переданные любой из этих функций.7778Вы можете неразрушающе проверить, было ли установлено исключение, с помощью [`PyErr_Occurred()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_Occurred). Она возвращает текущий объект исключения или *NULL*, если исключения не произошло. Обычно нет необходимости вызывать [`PyErr_Occurred()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_Occurred), чтобы узнать, произошла ли ошибка в вызове функции, так как об этом можно судить по возвращаемому значению.7980Когда функция *f*, вызывающая другую функцию *g*, обнаруживает, что последняя завершилась с ошибкой, *f* сама должна вернуть значение ошибки (обычно *NULL* или `-1`). Она *не* должна вызывать одну из функций `PyErr_*()` – одна уже была вызвана функцией *g*. Вызывающая сторона *f* затем также должна вернуть индикацию ошибки *своему* вызывающему, снова *без* вызова `PyErr_*()`, и так далее – наиболее детальная причина ошибки уже была сообщена функцией, которая первой её обнаружила. Как только ошибка достигает главного цикла интерпретатора Python, это прерывает выполняемый в данный момент код Python и пытается найти обработчик исключений, указанный программистом Python.8182(Бывают ситуации, когда модуль может дать более подробное сообщение об ошибке, вызвав другую функцию `PyErr_*()`, и в таких случаях это допустимо. Однако, как правило, это не обязательно и может привести к потере информации о причине ошибки: большинство операций могут завершиться ошибкой по разным причинам.)8384Чтобы проигнорировать исключение, установленное неудачным вызовом функции, условие исключения должно быть явно очищено вызовом [`PyErr_Clear()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_Clear). Единственный случай, когда C-код должен вызывать [`PyErr_Clear()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_Clear), – это если он не хочет передавать ошибку интерпретатору, а хочет полностью обработать её самостоятельно (возможно, попробовав что-то другое или сделав вид, что ничего не произошло).8586Каждый неудачный вызов `malloc()` должен быть преобразован в исключение – непосредственный вызывающий код `malloc()` (или `realloc()`) должен вызвать [`PyErr_NoMemory()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_NoMemory) и сам вернуть индикатор ошибки. Все функции, создающие объекты (например, [`PyLong_FromLong()`](https://python-all.ru/3.5/c-api/long.html#c.PyLong_FromLong)), уже делают это, поэтому данное замечание относится только к тем, кто вызывает `malloc()` напрямую.8788Также обратите внимание, что, за важным исключением [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) и подобных, функции, возвращающие целочисленный статус, обычно возвращают положительное значение или ноль в случае успеха и `-1` в случае ошибки, как системные вызовы Unix.8990Наконец, следует позаботиться об очистке мусора (вызвав [`Py_XDECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_XDECREF) или [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF) для уже созданных объектов) при возврате индикатора ошибки.9192Выбор, какое исключение возбуждать, полностью за вами. Существуют предварительно объявленные C-объекты, соответствующие всем встроенным исключениям Python, например `PyExc_ZeroDivisionError`, которые можно использовать напрямую. Конечно, следует выбирать исключения разумно – не стоит использовать `PyExc_TypeError` для обозначения того, что файл не удалось открыть (для этого вероятно следует использовать `PyExc_IOError`). Если что-то не так со списком аргументов, функция [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) обычно возбуждает `PyExc_TypeError`. Если значение аргумента должно находиться в определённом диапазоне или удовлетворять другим условиям, подходит `PyExc_ValueError`.9394Можно также определить новое исключение, уникальное для вашего модуля. Для этого обычно объявляется статическая объектная переменная в начале файла:9596```c97static PyObject *SpamError;98```99100и инициализируйте его в функции инициализации вашего модуля (`PyInit_spam()`) объектом исключения (пока опуская проверку ошибок):101102```c103PyMODINIT_FUNC104PyInit_spam(void)105{106 PyObject *m;107108 m = PyModule_Create(&spammodule);109 if (m == NULL)110 return NULL;111112 SpamError = PyErr_NewException("spam.error", NULL, NULL);113 Py_INCREF(SpamError);114 PyModule_AddObject(m, "error", SpamError);115 return m;116}117```118119Обратите внимание, что имя исключения в Python – `spam.error`. Функция [`PyErr_NewException()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_NewException) может создать класс с базовым классом [`Exception`](https://python-all.ru/3.5/library/exceptions.html#Exception) (если вместо *NULL* не передан другой класс), описанный в [Встроенные исключения](https://python-all.ru/3.5/library/exceptions.html#bltin-exceptions).120121Также обратите внимание, что переменная `SpamError` сохраняет ссылку на вновь созданный класс исключения; это сделано намеренно! Поскольку исключение может быть удалено из модуля внешним кодом, необходима собственная ссылка на класс, чтобы гарантировать, что он не будет отброшен, что приведёт к превращению `SpamError` в висячий указатель. Если он станет висячим указателем, C-код, возбуждающий исключение, может вызвать аварийный дамп или другие непреднамеренные побочные эффекты.122123Использование `PyMODINIT_FUNC` в качестве возвращаемого типа функции обсуждается далее в этом примере.124125Исключение `spam.error` может быть возбуждено в вашем модуле расширения с помощью вызова [`PyErr_SetString()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_SetString), как показано ниже:126127```c128static PyObject *129spam_system(PyObject *self, PyObject *args)130{131 const char *command;132 int sts;133134 if (!PyArg_ParseTuple(args, "s", &command))135 return NULL;136 sts = system(command);137 if (sts < 0) {138 PyErr_SetString(SpamError, "System command failed");139 return NULL;140 }141 return PyLong_FromLong(sts);142}143```144145## 1.3. Возвращение к примеру146147Вернёмся к нашей функции-примеру. Теперь вы должны понимать этот оператор:148149```c150if (!PyArg_ParseTuple(args, "s", &command))151 return NULL;152```153154Она возвращает *NULL* (индикатор ошибки для функций, возвращающих указатели на объекты), если в списке аргументов обнаружена ошибка, полагаясь на исключение, установленное [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple). В противном случае строковое значение аргумента копируется в локальную переменную `command`. Это присваивание указателя, и не следует изменять строку, на которую он указывает (поэтому в стандарте C переменная `command` должным образом должна быть объявлена как `const char *command`).155156Следующий оператор – это вызов функции Unix `system()` с передачей строки, полученной от [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple):157158```c159sts = system(command);160```161162Наша функция `spam.system()` должна вернуть значение `sts` как объект Python. Это делается с помощью функции [`PyLong_FromLong()`](https://python-all.ru/3.5/c-api/long.html#c.PyLong_FromLong).163164```c165return PyLong_FromLong(sts);166```167168В данном случае она вернёт целочисленный объект. (Да, даже целые числа в Python – это объекты в куче!)169170Если у вас есть C-функция, которая не возвращает полезного аргумента (функция, возвращающая `void`), соответствующая функция Python должна возвращать `None`. Для этого требуется такая идиома (она реализована макросом [`Py_RETURN_NONE`](https://python-all.ru/3.5/c-api/none.html#c.Py_RETURN_NONE)):171172```c173Py_INCREF(Py_None);174return Py_None;175```176177[`Py_None`](https://python-all.ru/3.5/c-api/none.html#c.Py_None) – это имя в C для специального объекта Python `None`. Это настоящий объект Python, а не указатель *NULL*, который, как мы видели, в большинстве контекстов означает «ошибку».178179## 1.4. Таблица методов модуля и функция инициализации180181Я обещал показать, как `spam_system()` вызывается из программ Python. Сначала нужно перечислить её имя и адрес в «таблице методов»:182183```c184static PyMethodDef SpamMethods[] = {185 ...186 {"system", spam_system, METH_VARARGS,187 "Execute a shell command."},188 ...189 {NULL, NULL, 0, NULL} /* Страж */190};191```192193Обратите внимание на третью запись (`METH_VARARGS`). Это флаг, сообщающий интерпретатору соглашение о вызове для C-функции. Обычно он должен быть `METH_VARARGS` или `METH_VARARGS | METH_KEYWORDS`; значение `0` означает, что используется устаревший вариант [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple).194195При использовании только `METH_VARARGS` функция должна ожидать, что параметры уровня Python будут переданы в виде кортежа, пригодного для разбора через [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple); подробнее об этой функции сказано ниже.196197Флаг [`METH_KEYWORDS`](https://python-all.ru/3.5/c-api/structures.html#METH_KEYWORDS) может быть установлен в третьем поле, если функции должны передаваться ключевые аргументы. В этом случае C-функция должна принимать третий параметр `PyObject *` – словарь ключевых слов. Для разбора аргументов такой функции используйте [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords).198199Таблица методов должна быть указана в структуре определения модуля:200201```c202static struct PyModuleDef spammodule = {203 PyModuleDef_HEAD_INIT,204 "spam", /* имя модуля */205 spam_doc, /* документация модуля, может быть NULL */206 -1, /* размер состояния модуля для каждого интерпретатора,207 или -1, если модуль хранит состояние в глобальных переменных. */208 SpamMethods209};210```211212Эта структура, в свою очередь, должна быть передана интерпретатору в функции инициализации модуля. Функция инициализации должна называться `PyInit_name()`, где *name* – имя модуля, и должна быть единственным не-`static` элементом, определённым в файле модуля:213214```c215PyMODINIT_FUNC216PyInit_spam(void)217{218 return PyModule_Create(&spammodule);219}220```221222Обратите внимание, что PyMODINIT\_FUNC объявляет функцию с типом возвращаемого значения `PyObject *`, объявляет все необходимые для платформы специальные объявления компоновки, а для C++ объявляет функцию как `extern "C"`.223224Когда программа Python впервые импортирует модуль `spam`, вызывается `PyInit_spam()`. (См. ниже замечания о встраивании Python.) Она вызывает [`PyModule_Create()`](https://python-all.ru/3.5/c-api/module.html#c.PyModule_Create), которая возвращает объект модуля, и вставляет объекты встроенных функций в только что созданный модуль на основе таблицы (массива структур [`PyMethodDef`](https://python-all.ru/3.5/c-api/structures.html#c.PyMethodDef)), найденной в определении модуля. [`PyModule_Create()`](https://python-all.ru/3.5/c-api/module.html#c.PyModule_Create) возвращает указатель на созданный объект модуля. Она может завершиться фатальной ошибкой для некоторых ошибок или вернуть *NULL*, если модуль не удалось инициализировать удовлетворительно. Функция init должна вернуть объект модуля своему вызывающему, чтобы затем он был вставлен в `sys.modules`.225226При встраивании Python функция `PyInit_spam()` не вызывается автоматически, если только в таблице `PyImport_Inittab` нет соответствующей записи. Чтобы добавить модуль в таблицу инициализации, используйте [`PyImport_AppendInittab()`](https://python-all.ru/3.5/c-api/import.html#c.PyImport_AppendInittab), за которым опционально следует импорт модуля:227228```c229int230main(int argc, char *argv[])231{232 wchar_t *program = Py_DecodeLocale(argv[0], NULL);233 if (program == NULL) {234 fprintf(stderr, "Fatal error: cannot decode argv[0]\n");235 exit(1);236 }237238 /* Добавить встроенный модуль перед Py_Initialize */239 PyImport_AppendInittab("spam", PyInit_spam);240241 /* Передать argv[0] интерпретатору Python */242 Py_SetProgramName(program);243244 /* Инициализировать интерпретатор Python. Обязательно. */245 Py_Initialize();246247 /* Опционально импортировать модуль; в качестве альтернативы,248 импорт может быть отложен до тех пор, пока встроенный скрипт249 не импортирует его. */250 PyImport_ImportModule("spam");251252 ...253254 PyMem_RawFree(program);255 return 0;256}257```258259> **Примечание**260>261> Удаление записей из `sys.modules` или импорт скомпилированных модулей в несколько интерпретаторов в рамках одного процесса (или после `fork()` без промежуточного `exec()`) может создать проблемы для некоторых модулей расширения. Авторам модулей расширения следует проявлять осторожность при инициализации внутренних структур данных.262263Более объёмный пример модуля включён в дистрибутив исходных текстов Python как `Modules/xxmodule.c`. Этот файл можно использовать как шаблон или просто прочитать как пример.264265> **Примечание**266>267> В отличие от нашего примера `spam`, `xxmodule` использует *многофазную инициализацию* (новая возможность Python 3.5), когда структура PyModuleDef возвращается из `PyInit_spam`, а создание модуля оставляется на усмотрение механизма импорта. Подробнее о многофазной инициализации см. [**PEP 489**](https://python-all.ru/3.5/extending/extending.html).268269## 1.5. Компиляция и компоновка270271Перед использованием нового расширения необходимо сделать ещё две вещи: скомпилировать и скомпоновать его с системой Python. Если используется динамическая загрузка, детали могут зависеть от стиля динамической загрузки, применяемого в вашей системе; см. главы о сборке модулей расширения (глава [Сборка расширений на C и C++](https://python-all.ru/3.5/extending/building.html#building)) и дополнительную информацию, относящуюся только к сборке в Windows (глава [Сборка расширений на C и C++ в Windows](https://python-all.ru/3.5/extending/windows.html#building-on-windows)) для получения более подробных сведений.272273Если динамическая загрузка недоступна или вы хотите сделать модуль постоянной частью интерпретатора Python, придётся изменить конфигурацию и пересобрать интерпретатор. К счастью, в Unix это очень просто: поместите ваш файл (например, `spammodule.c`) в каталог `Modules/` распакованного дистрибутива исходных кодов, добавьте строку в файл `Modules/Setup.local`, описывающую ваш файл:274275```c276spam spammodule.o277```278279и пересобрать интерпретатор, выполнив **make** в корневом каталоге. Также можно выполнить **make** в подкаталоге `Modules/`, но тогда сначала нужно пересобрать `Makefile` там, выполнив '**make** Makefile'. (Это необходимо каждый раз при изменении файла `Setup`.)280281Если ваш модуль требует дополнительных библиотек для компоновки, их также можно перечислить в строке конфигурационного файла, например:282283```c284spam spammodule.o -lX11285```286287## 1.6. Вызов функций Python из C288289До сих пор мы сосредотачивались на том, как сделать C-функции вызываемыми из Python. Обратное также полезно: вызов функций Python из C. Это особенно актуально для библиотек, поддерживающих так называемые «колбэки». Если C-интерфейс использует колбэки, то эквивалентный Python часто должен предоставить механизм колбэков программисту на Python; реализация потребует вызова функций Python-колбэков из C-колбэка. Можно представить и другие варианты использования.290291К счастью, интерпретатор Python легко вызывается рекурсивно, и существует стандартный интерфейс для вызова функции Python. (Я не буду останавливаться на том, как вызвать анализатор Python с определённой строкой на входе – если вам интересно, взгляните на реализацию опции командной строки [`-c`](https://python-all.ru/3.5/using/cmdline.html#cmdoption-c) в `Modules/main.c` из исходного кода Python.)292293Вызвать функцию Python легко. Во-первых, программа на Python должна каким-то образом передать вам объект функции Python. Вы должны предоставить функцию (или какой-то другой интерфейс) для этого. Когда эта функция вызывается, сохраните указатель на объект функции Python (не забудьте [`Py_INCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_INCREF) её!) в глобальной переменной – или где сочтёте нужным. Например, следующая функция может быть частью определения модуля:294295```c296static PyObject *my_callback = NULL;297298static PyObject *299my_set_callback(PyObject *dummy, PyObject *args)300{301 PyObject *result = NULL;302 PyObject *temp;303304 if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {305 if (!PyCallable_Check(temp)) {306 PyErr_SetString(PyExc_TypeError, "parameter must be callable");307 return NULL;308 }309 Py_XINCREF(temp); /* Добавить ссылку на новый колбэк */310 Py_XDECREF(my_callback); /* Освободить предыдущий колбэк */311 my_callback = temp; /* Запомнить новый колбэк */312 /* Шаблон для возврата None */313 Py_INCREF(Py_None);314 result = Py_None;315 }316 return result;317}318```319320Эта функция должна быть зарегистрирована в интерпретаторе с помощью флага [`METH_VARARGS`](https://python-all.ru/3.5/c-api/structures.html#METH_VARARGS); это описано в разделе [Таблица методов модуля и функция инициализации](https://python-all.ru/3.5/extending/extending.html#methodtable). Функция [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) и её аргументы описаны в разделе [Извлечение параметров в функциях расширения](https://python-all.ru/3.5/extending/extending.html#parsetuple).321322Макросы [`Py_XINCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_XINCREF) и [`Py_XDECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_XDECREF) увеличивают/уменьшают счётчик ссылок объекта и безопасны при наличии указателей *NULL* (но обратите внимание, что *temp* в этом контексте не будет *NULL*). Дополнительная информация о них в разделе [Счётчики ссылок](https://python-all.ru/3.5/extending/extending.html#refcounts).323324Позже, когда придёт время вызвать функцию, вызывается C-функция [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject). Эта функция принимает два аргумента, оба указатели на произвольные объекты Python: функцию Python и список аргументов. Список аргументов всегда должен быть объектом кортежа, длина которого равна количеству аргументов. Чтобы вызвать функцию Python без аргументов, передайте NULL или пустой кортеж; для вызова с одним аргументом передайте кортеж из одного элемента. [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue) возвращает кортеж, если строка формата состоит из нуля или более кодов формата в круглых скобках. Например:325326```c327int arg;328PyObject *arglist;329PyObject *result;330...331arg = 123;332...333/* Вызов колбэка */334arglist = Py_BuildValue("(i)", arg);335result = PyObject_CallObject(my_callback, arglist);336Py_DECREF(arglist);337```338339[`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject) возвращает указатель на объект Python: это возвращаемое значение функции Python. [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject) является «нейтральным по отношению к подсчёту ссылок» в отношении своих аргументов. В примере был создан новый кортеж, служащий списком аргументов, который [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF)-ится сразу после вызова [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject).340341Возвращаемое значение [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject) является «новым»: это либо совершенно новый объект, либо существующий объект, чей счётчик ссылок был увеличен. Поэтому, если вы не хотите сохранять его в глобальной переменной, вы должны каким-то образом [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF) результат, даже (особенно!) если вас не интересует его значение.342343Однако перед этим важно проверить, что возвращаемое значение не равно *NULL*. Если это так, функция Python завершилась возбуждением исключения. Если C-код, вызвавший [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject), вызван из Python, он должен вернуть индикацию ошибки своему вызывающему из Python, чтобы интерпретатор мог вывести стек вызовов или чтобы вызывающий код Python мог обработать исключение. Если это невозможно или нежелательно, исключение следует очистить вызовом [`PyErr_Clear()`](https://python-all.ru/3.5/c-api/exceptions.html#c.PyErr_Clear). Например:344345```c346if (result == NULL)347 return NULL; /* Передать ошибку обратно */348...use result...349Py_DECREF(result);350```351352В зависимости от желаемого интерфейса к Python-функции обратного вызова, вам также может потребоваться предоставить список аргументов для [`PyObject_CallObject()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_CallObject). В некоторых случаях список аргументов также предоставляется программой на Python через тот же интерфейс, который задал функцию обратного вызова. Затем его можно сохранить и использовать так же, как объект функции. В других случаях вам может потребоваться создать новый кортеж для передачи в качестве списка аргументов. Самый простой способ сделать это – вызвать [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue). Например, если вы хотите передать целочисленный код события, можно использовать следующий код:353354```c355PyObject *arglist;356...357arglist = Py_BuildValue("(l)", eventcode);358result = PyObject_CallObject(my_callback, arglist);359Py_DECREF(arglist);360if (result == NULL)361 return NULL; /* Передать ошибку обратно */362/* Здесь, возможно, используется результат */363Py_DECREF(result);364```365366Обратите внимание на размещение `Py_DECREF(arglist)` сразу после вызова, перед проверкой ошибки! Также обратите внимание, что строго говоря, этот код не полон: [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue) может исчерпать память, и это должно быть проверено.367368Вы также можете вызвать функцию с именованными аргументами, используя [`PyObject_Call()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_Call), который поддерживает аргументы и именованные аргументы. Как в примере выше, мы используем [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue) для создания словаря.369370```c371PyObject *dict;372...373dict = Py_BuildValue("{s:i}", "name", val);374result = PyObject_Call(my_callback, NULL, dict);375Py_DECREF(dict);376if (result == NULL)377 return NULL; /* Передать ошибку обратно */378/* Здесь, возможно, используется результат */379Py_DECREF(result);380```381382## 1.7. Извлечение параметров в функциях расширения383384Функция [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) объявляется следующим образом:385386```c387int PyArg_ParseTuple(PyObject *arg, const char *format, ...);388```389390Аргумент *arg* должен быть объектом кортежа, содержащим список аргументов, переданных из Python в C-функцию. Аргумент *format* должен быть строкой формата, чей синтаксис описан в разделе [Разбор аргументов и построение значений](https://python-all.ru/3.5/c-api/arg.html#arg-parsing) справочного руководства Python/C API. Остальные аргументы должны быть адресами переменных, чей тип определяется строкой формата.391392Обратите внимание, что хотя [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple) проверяет, что аргументы Python имеют требуемые типы, он не может проверить корректность адресов C-переменных, переданных в вызов: если там допущены ошибки, код, вероятно, аварийно завершится или, по крайней мере, перезапишет случайные биты в памяти. Поэтому следует быть осторожным!393394Обратите внимание, что любые ссылки на объекты Python, передаваемые вызывающему, являются *заимствованными* ссылками; уменьшать их счётчик ссылок не следует.395396Несколько примеров вызовов:397398```c399#define PY_SSIZE_T_CLEAN /* Сделать так, чтобы "s#" использовало Py_ssize_t, а не int. */400#include <Python.h>401```402403```c404int ok;405int i, j;406long k, l;407const char *s;408Py_ssize_t size;409410ok = PyArg_ParseTuple(args, ""); /* Без аргументов */411 /* Вызов Python: f() */412```413414```c415ok = PyArg_ParseTuple(args, "s", &s); /* Строка */416 /* Возможный вызов Python: f('whoops!') */417```418419```c420ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Два длинных целых и строка */421 /* Возможный вызов Python: f(1, 2, 'three') */422```423424```c425ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);426 /* Пара int и строка, размер которой также возвращается */427 /* Возможный вызов Python: f((1, 2), 'three') */428```429430```c431{432 const char *file;433 const char *mode = "r";434 int bufsize = 0;435 ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);436 /* Строка и, опционально, ещё одна строка и целое число */437 /* Возможные вызовы Python:438 f('spam')439 f('spam', 'w')440 f('spam', 'wb', 100000) */441}442```443444```c445{446 int left, top, right, bottom, h, v;447 ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",448 &left, &top, &right, &bottom, &h, &v);449 /* Прямоугольник и точка */450 /* Возможный вызов Python:451 f(((0, 0), (400, 300)), (10, 10)) */452}453```454455```c456{457 Py_complex c;458 ok = PyArg_ParseTuple(args, "D:myfunction", &c);459 /* комплексное число, также предоставляющее имя функции для сообщений об ошибках */460 /* Возможный вызов Python: myfunction(1+2j) */461}462```463464## 1.8. Именованные параметры для функций расширения465466Функция [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) объявляется следующим образом:467468```c469int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,470 const char *format, char *kwlist[], ...);471```472473Параметры *arg* и *format* идентичны параметрам функции [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple). Параметр *kwdict* – это словарь ключевых слов, полученный в качестве третьего параметра от среды выполнения Python. Параметр *kwlist* – это список строк, завершающийся *NULL*, который идентифицирует параметры; имена сопоставляются с информацией о типах из *format* слева направо. В случае успеха [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) возвращает true, в противном случае возвращает false и возбуждает соответствующее исключение.474475> **Примечание**476>477> Вложенные кортежи не могут быть разобраны при использовании именованных аргументов! Именованные параметры, переданные, но отсутствующие в *kwlist*, приведут к возбуждению [`TypeError`](https://python-all.ru/3.5/library/exceptions.html#TypeError).478479Вот пример модуля, использующего ключевые слова, основанный на примере Джеффа Филбрика ([philbrick@hks.com](https://python-all.ru/3.5/extending/extending.html)):480481```c482#include "Python.h"483484static PyObject *485keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)486{487 int voltage;488 char *state = "a stiff";489 char *action = "voom";490 char *type = "Norwegian Blue";491492 static char *kwlist[] = {"voltage", "state", "action", "type", NULL};493494 if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,495 &voltage, &state, &action, &type))496 return NULL;497498 printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",499 action, voltage);500 printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);501502 Py_RETURN_NONE;503}504505static PyMethodDef keywdarg_methods[] = {506 /* Приведение функции необходимо, поскольку значения PyCFunction507 * принимают только два параметра PyObject*, а keywdarg_parrot() принимает508 * три.509 */510 {"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,511 "Print a lovely skit to standard output."},512 {NULL, NULL, 0, NULL} /* сторожевое значение */513};514515static struct PyModuleDef keywdargmodule = {516 PyModuleDef_HEAD_INIT,517 "keywdarg",518 NULL,519 -1,520 keywdarg_methods521};522523PyMODINIT_FUNC524PyInit_keywdarg(void)525{526 return PyModule_Create(&keywdargmodule);527}528```529530## 1.9. Построение произвольных значений531532Эта функция является аналогом [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple). Она объявляется следующим образом:533534```c535PyObject *Py_BuildValue(const char *format, ...);536```537538Она распознаёт набор форматных единиц, аналогичный распознаваемым [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple), но аргументы (которые являются входными для функции, а не выходными) не должны быть указателями, только значениями. Она возвращает новый объект Python, подходящий для возврата из C-функции, вызванной из Python.539540Одно отличие от [`PyArg_ParseTuple()`](https://python-all.ru/3.5/c-api/arg.html#c.PyArg_ParseTuple): последняя требует, чтобы её первый аргумент был кортежем (поскольку списки аргументов Python внутренне всегда представлены как кортежи), тогда как [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue) не всегда строит кортеж. Она строит кортеж, только если строка формата содержит две или более форматных единиц. Если строка формата пуста, она возвращает `None`; если она содержит ровно одну форматную единицу, она возвращает объект, описываемый этой единицей. Чтобы принудительно вернуть кортеж размера 0 или 1, строку формата следует заключить в скобки.541542Примеры (слева вызов, справа результирующее значение Python):543544```text545Py_BuildValue("") None546Py_BuildValue("i", 123) 123547Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)548Py_BuildValue("s", "hello") 'hello'549Py_BuildValue("y", "hello") b'hello'550Py_BuildValue("ss", "hello", "world") ('hello', 'world')551Py_BuildValue("s#", "hello", 4) 'hell'552Py_BuildValue("y#", "hello", 4) b'hell'553Py_BuildValue("()") ()554Py_BuildValue("(i)", 123) (123,)555Py_BuildValue("(ii)", 123, 456) (123, 456)556Py_BuildValue("(i,i)", 123, 456) (123, 456)557Py_BuildValue("[i,i]", 123, 456) [123, 456]558Py_BuildValue("{s:i,s:i}",559 "abc", 123, "def", 456) {'abc': 123, 'def': 456}560Py_BuildValue("((ii)(ii)) (ii)",561 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))562```563564## 1.10. Подсчёт ссылок565566В таких языках, как C или C++, программист отвечает за динамическое выделение и освобождение памяти в куче. В C это делается с помощью функций `malloc()` и `free()`. В C++ операторы `new` и `delete` используются с тем же смыслом; дальнейшее обсуждение ограничивается случаем C.567568Каждый блок памяти, выделенный с помощью `malloc()`, в конечном итоге должен быть возвращён в пул доступной памяти ровно одним вызовом `free()`. Очень важно вызвать `free()` в нужный момент. Если адрес блока будет забыт, а `free()` для него не вызван, занимаемая им память не может быть использована повторно до завершения программы. Это называется *утечкой памяти*. С другой стороны, если программа вызывает `free()` для блока, а затем продолжает его использовать, это создаёт конфликт с повторным использованием блока через другой вызов `malloc()`. Это называется *использованием освобождённой памяти*. Это имеет такие же плохие последствия, как обращение к неинициализированным данным – дампы памяти, неверные результаты, загадочные сбои.569570Распространённые причины утечек памяти – нестандартные пути выполнения кода. Например, функция может выделить блок памяти, выполнить некоторые вычисления, а затем снова освободить блок. Теперь изменение требований к функции может добавить в вычисление проверку, которая обнаруживает ошибочную ситуацию и может привести к преждевременному возврату из функции. Легко забыть освободить выделенный блок памяти при таком преждевременном выходе, особенно если он был добавлен в код позднее. Такие утечки, будучи однажды внесёнными, часто остаются незамеченными долгое время: ошибочный выход происходит лишь в небольшой доле всех вызовов, а на большинстве современных машин достаточно виртуальной памяти, поэтому утечка становится заметна только в долго работающем процессе, который часто использует функцию с утечкой. Следовательно, важно предотвращать утечки, используя соглашение о кодировании или стратегию, которая минимизирует такого рода ошибки.571572Поскольку Python активно использует `malloc()` и `free()`, ему нужна стратегия для предотвращения как утечек памяти, так и использования освобождённой памяти. Выбранный метод называется *подсчётом ссылок*. Принцип прост: каждый объект содержит счётчик, который увеличивается, когда ссылка на объект сохраняется где-либо, и уменьшается, когда ссылка на него удаляется. Когда счётчик достигает нуля, последняя ссылка на объект удалена, и объект освобождается.573574Альтернативная стратегия называется *автоматической сборкой мусора*. (Иногда подсчёт ссылок тоже называют стратегией сборки мусора, поэтому я использую слово «автоматическая», чтобы различать их.) Большое преимущество автоматической сборки мусора в том, что пользователю не нужно вызывать `free()` явно. (Другое заявляемое преимущество – улучшение скорости или использования памяти, однако это не факт.) Недостаток в том, что для C не существует по-настоящему переносимого автоматического сборщика мусора, в то время как подсчёт ссылок можно реализовать переносимо (при условии, что функции `malloc()` и `free()` доступны – что гарантирует стандарт C). Возможно, когда-нибудь для C появится достаточно переносимый автоматический сборщик мусора. До тех пор придётся мириться с подсчётом ссылок.575576Хотя Python использует традиционную реализацию подсчёта ссылок, он также предлагает детектор циклов, который обнаруживает циклические ссылки. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок; они являются слабым местом сборки мусора, реализованной только с помощью подсчёта ссылок. Циклические ссылки состоят из объектов, которые содержат (возможно, косвенные) ссылки на самих себя, так что каждый объект в цикле имеет ненулевой счётчик ссылок. Типичные реализации подсчёта ссылок не могут освободить память, принадлежащую объектам в циклической ссылке или на которые есть ссылки из объектов в цикле, даже если на сам цикл больше нет ссылок.577578Детектор циклов способен обнаруживать циклический мусор и может его освобождать. Модуль [`gc`](https://python-all.ru/3.5/library/gc.html#module-gc) предоставляет возможность запустить детектор (функция [`collect()`](https://python-all.ru/3.5/library/gc.html#gc.collect)), а также интерфейсы конфигурации и возможность отключить детектор во время выполнения. Детектор циклов считается необязательным компонентом; хотя он включён по умолчанию, его можно отключить при сборке с помощью параметра `--without-cycle-gc` скрипта **configure** на платформах Unix (включая Mac OS X). Если детектор циклов отключён таким образом, модуль [`gc`](https://python-all.ru/3.5/library/gc.html#module-gc) будет недоступен.579580### 1.10.1. Подсчёт ссылок в Python581582Существуют два макроса, `Py_INCREF(x)` и `Py_DECREF(x)`, которые управляют увеличением и уменьшением счётчика ссылок. [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF) также освобождает объект, когда счётчик достигает нуля. Для гибкости он не вызывает `free()` напрямую – вместо этого он выполняет вызов через указатель на функцию в *объекте типа* объекта. Для этой цели (и других) каждый объект также содержит указатель на свой объект типа.583584Остаётся главный вопрос: когда использовать `Py_INCREF(x)` и `Py_DECREF(x)`? Сначала введём несколько терминов. Никто не «владеет» объектом; однако можно *владеть ссылкой* на объект. Счётчик ссылок объекта теперь определяется как количество принадлежащих (owned) ссылок на него. Владелец ссылки отвечает за вызов [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF), когда ссылка больше не нужна. Владение ссылкой может быть передано. Существует три способа распорядиться принадлежащей ссылкой: передать её, сохранить или вызвать [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF). Забыть распорядиться принадлежащей ссылкой – значит создать утечку памяти.585586Также можно *заимствовать* [\[2\]](https://python-all.ru/3.5/extending/extending.html#id6) ссылку на объект. Заимствователь ссылки не должен вызывать [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF). Заимствователь не должен удерживать объект дольше, чем владелец, у которого он был заимствован. Использование заимствованной ссылки после того, как владелец от неё избавился, рискует привести к использованию освобождённой памяти, и этого следует полностью избегать. [\[3\]](https://python-all.ru/3.5/extending/extending.html#id7)587588Преимущество заимствования ссылки перед владением в том, что не нужно заботиться о распоряжении ссылкой на всех возможных путях выполнения кода – иными словами, с заимствованной ссылкой нет риска утечки при преждевременном выходе. Недостаток заимствования по сравнению с владением в том, что существуют некоторые тонкие ситуации, когда в, казалось бы, корректном коде заимствованная ссылка может быть использована после того, как владелец, у которого она была заимствована, фактически распорядился ею.589590Заимствованная ссылка может быть превращена в собственную путём вызова [`Py_INCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_INCREF). Это не влияет на статус владельца, у которого была заимствована ссылка – создаётся новая собственная ссылка, и на нового владельца возлагаются все обязанности владельца (новый владелец должен правильно распорядиться ссылкой, равно как и предыдущий владелец).591592### 1.10.2. Правила владения593594Всякий раз, когда ссылка на объект передаётся в функцию или из неё, является частью спецификации интерфейса функции, передаётся ли владение вместе со ссылкой или нет.595596Большинство функций, возвращающих ссылку на объект, передают вместе с ней и владение. В частности, все функции, чья задача – создать новый объект, такие как [`PyLong_FromLong()`](https://python-all.ru/3.5/c-api/long.html#c.PyLong_FromLong) и [`Py_BuildValue()`](https://python-all.ru/3.5/c-api/arg.html#c.Py_BuildValue), передают владение получателю. Даже если объект на самом деле не новый, всё равно получается владение новой ссылкой на этот объект. Например, [`PyLong_FromLong()`](https://python-all.ru/3.5/c-api/long.html#c.PyLong_FromLong) поддерживает кеш популярных значений и может вернуть ссылку на кешированный элемент.597598Многие функции, извлекающие объекты из других объектов, также передают владение вместе со ссылкой, например [`PyObject_GetAttrString()`](https://python-all.ru/3.5/c-api/object.html#c.PyObject_GetAttrString). Однако здесь картина менее ясна, поскольку несколько распространённых подпрограмм являются исключениями: [`PyTuple_GetItem()`](https://python-all.ru/3.5/c-api/tuple.html#c.PyTuple_GetItem), [`PyList_GetItem()`](https://python-all.ru/3.5/c-api/list.html#c.PyList_GetItem), [`PyDict_GetItem()`](https://python-all.ru/3.5/c-api/dict.html#c.PyDict_GetItem) и [`PyDict_GetItemString()`](https://python-all.ru/3.5/c-api/dict.html#c.PyDict_GetItemString) – все они возвращают ссылки, которые заимствуются из кортежа, списка или словаря.599600Функция [`PyImport_AddModule()`](https://python-all.ru/3.5/c-api/import.html#c.PyImport_AddModule) также возвращает заимствованную ссылку, даже если она на самом деле создаёт возвращаемый объект: это возможно, потому что собственная ссылка на объект хранится в `sys.modules`.601602Когда вы передаёте ссылку на объект в другую функцию, в общем случае функция заимствует у вас эту ссылку – если ей нужно сохранить её, она использует [`Py_INCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_INCREF), чтобы стать независимым владельцем. Из этого правила есть ровно два важных исключения: [`PyTuple_SetItem()`](https://python-all.ru/3.5/c-api/tuple.html#c.PyTuple_SetItem) и [`PyList_SetItem()`](https://python-all.ru/3.5/c-api/list.html#c.PyList_SetItem). Эти функции принимают владение переданным им объектом – даже в случае неудачи! (Обратите внимание, что [`PyDict_SetItem()`](https://python-all.ru/3.5/c-api/dict.html#c.PyDict_SetItem) и подобные ему функции не принимают владение – они «нормальные».)603604Когда C-функция вызывается из Python, она заимствует ссылки на свои аргументы у вызывающей стороны. Вызывающая сторона владеет ссылкой на объект, поэтому время жизни заимствованной ссылки гарантировано до возврата из функции. Только если такую заимствованную ссылку необходимо сохранить или передать дальше, она должна быть превращена в собственную ссылку с помощью вызова [`Py_INCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_INCREF).605606Ссылка на объект, возвращаемая из C-функции, вызванной из Python, должна быть собственной – владение передаётся от функции её вызывающей стороне.607608### 1.10.3. Тонкий лёд609610Есть несколько ситуаций, когда безобидное, на первый взгляд, использование заимствованной ссылки может привести к проблемам. Все они связаны с неявными вызовами интерпретатора, которые могут заставить владельца ссылки освободить её.611612Первый и самый важный случай, о котором стоит знать, – это использование [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF) на постороннем объекте, пока заимствована ссылка на элемент списка. Например:613614```c615void616bug(PyObject *list)617{618 PyObject *item = PyList_GetItem(list, 0);619620 PyList_SetItem(list, 1, PyLong_FromLong(0L));621 PyObject_Print(item, stdout, 0); /* ОШИБКА! */622}623```624625Эта функция сначала заимствует ссылку на `list[0]`, затем заменяет `list[1]` значением `0` и наконец печатает заимствованную ссылку. Выглядит безобидно, правда? Но это не так!626627Проследим поток управления внутри [`PyList_SetItem()`](https://python-all.ru/3.5/c-api/list.html#c.PyList_SetItem). Список владеет ссылками на все свои элементы, поэтому при замене элемента 1 он должен избавиться от исходного элемента 1. Предположим теперь, что исходный элемент 1 был экземпляром пользовательского класса, и далее предположим, что в классе определён метод [`__del__()`](https://python-all.ru/3.5/reference/datamodel.html#object.__del__). Если у этого экземпляра класса счётчик ссылок равен 1, при его удалении будет вызван метод [`__del__()`](https://python-all.ru/3.5/reference/datamodel.html#object.__del__).628629Поскольку метод [`__del__()`](https://python-all.ru/3.5/reference/datamodel.html#object.__del__) написан на Python, он может выполнять произвольный код Python. Может ли он каким-то образом аннулировать ссылку на `item` в `bug()`? Ещё как! Если предположить, что список, переданный в `bug()`, доступен методу [`__del__()`](https://python-all.ru/3.5/reference/datamodel.html#object.__del__), то он может выполнить инструкцию вроде `del list[0]`, и если это была последняя ссылка на этот объект, то он освободит связанную с ним память, тем самым аннулировав `item`.630631Решение, если знать источник проблемы, простое: временно увеличить счётчик ссылок. Правильная версия функции выглядит так:632633```c634void635no_bug(PyObject *list)636{637 PyObject *item = PyList_GetItem(list, 0);638639 Py_INCREF(item);640 PyList_SetItem(list, 1, PyLong_FromLong(0L));641 PyObject_Print(item, stdout, 0);642 Py_DECREF(item);643}644```645646Это реальная история. В старой версии Python были варианты этой ошибки, и кому-то пришлось потратить много времени в отладчике C, чтобы выяснить, почему его методы [`__del__()`](https://python-all.ru/3.5/reference/datamodel.html#object.__del__) не работали…647648Второй случай проблем с заимствованной ссылкой связан с потоками. Обычно несколько потоков в интерпретаторе Python не мешают друг другу, поскольку существует глобальная блокировка, защищающая всё пространство объектов Python. Однако эту блокировку можно временно отпустить с помощью макроса [`Py_BEGIN_ALLOW_THREADS`](https://python-all.ru/3.5/c-api/init.html#c.Py_BEGIN_ALLOW_THREADS) и снова захватить с помощью [`Py_END_ALLOW_THREADS`](https://python-all.ru/3.5/c-api/init.html#c.Py_END_ALLOW_THREADS). Это обычная практика вокруг блокирующих операций ввода-вывода, чтобы дать другим потокам использовать процессор во время ожидания завершения ввода-вывода. Очевидно, следующая функция имеет ту же проблему, что и предыдущая:649650```c651void652bug(PyObject *list)653{654 PyObject *item = PyList_GetItem(list, 0);655 Py_BEGIN_ALLOW_THREADS656 ...some blocking I/O call...657 Py_END_ALLOW_THREADS658 PyObject_Print(item, stdout, 0); /* ОШИБКА! */659}660```661662### 1.10.4. NULL-указатели663664В общем, функции, принимающие ссылки на объекты в качестве аргументов, не ожидают, что им будут передавать указатели *NULL*, и при такой передаче может произойти аварийное завершение (дамп ядра) или возникнуть последующие сбои. Функции, возвращающие ссылки на объекты, обычно возвращают *NULL* только для указания на то, что произошло исключение. Причина, по которой не проверяются аргументы на *NULL*, заключается в том, что функции часто передают полученные объекты другим функциям – если бы каждая функция проверяла *NULL*, было бы много избыточных проверок и код работал бы медленнее.665666Лучше проверять на *NULL* только у «источника»: когда получен указатель, который может быть *NULL*, например, от `malloc()` или от функции, которая может возбуждать исключение.667668Макросы [`Py_INCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_INCREF) и [`Py_DECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_DECREF) не проверяют указатели на *NULL* – однако их варианты [`Py_XINCREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_XINCREF) и [`Py_XDECREF()`](https://python-all.ru/3.5/c-api/refcounting.html#c.Py_XDECREF) делают это.669670Макросы для проверки конкретного типа объекта (`Pytype_Check()`) не проверяют указатели на *NULL* – опять же, есть много кода, который вызывает несколько таких макросов подряд для проверки объекта на соответствие различным ожидаемым типам, и это привело бы к избыточным проверкам. Нет вариантов с проверкой *NULL*.671672Механизм вызова функций C гарантирует, что список аргументов, передаваемый функциям C (`args` в примерах), никогда не равен *NULL* – более того, он гарантирует, что это всегда кортеж. [\[4\]](https://python-all.ru/3.5/extending/extending.html#id8)673674Серьёзной ошибкой является допустить, чтобы указатель *NULL* «попал» к пользователю Python.675676## 1.11. Написание расширений на C++677678Модули расширения можно писать на C++. Применяются некоторые ограничения. Если главная программа (интерпретатор Python) компилируется и компонуется C- компилятором, нельзя использовать глобальные или статические объекты с конструкторами. Это не проблема, если главная программа компонуется C++-компилятором. Функции, которые будут вызываться интерпретатором Python (в частности, функции инициализации модулей), должны быть объявлены с помощью `extern "C"`. Нет необходимости заключать заголовочные файлы Python в `extern "C" {...}` – они уже используют эту форму, если определён символ `__cplusplus` (все современные C++-компиляторы определяют этот символ).679680## 1.12. Предоставление C API для модуля расширения681682Многие модули расширения просто предоставляют новые функции и типы для использования из Python, но иногда код в модуле расширения может быть полезен для других модулей расширения. Например, модуль расширения может реализовать тип «коллекция», который работает как список без порядка. Подобно тому, как стандартный тип списка Python имеет C API, позволяющий модулям расширения создавать списки и управлять ими, этот новый тип коллекции должен иметь набор C-функций для прямого управления из других модулей расширения.683684На первый взгляд задача кажется простой: достаточно написать функции (не объявляя их `static`, разумеется), предоставить соответствующий заголовочный файл и документировать C API. И действительно, это бы сработало, если бы все модули расширения всегда статически компоновались с интерпретатором Python. Однако, когда модули используются как разделяемые библиотеки, символы, определённые в одном модуле, могут быть не видны другому модулю. Детали видимости зависят от операционной системы; одни системы используют единое глобальное пространство имён для интерпретатора Python и всех модулей расширения (например, Windows), а другие требуют явного списка импортируемых символов на этапе компоновки модуля (например, AIX) или предлагают выбор разных стратегий (большинство Unix-систем). И даже если символы глобально видны, модуль, функции которого нужно вызвать, может быть ещё не загружен!685686Поэтому для обеспечения переносимости не следует делать предположений о видимости символов. Это означает, что все символы в модулях расширения должны быть объявлены `static`, за исключением функции инициализации модуля, чтобы избежать конфликтов имён с другими модулями расширения (как обсуждается в разделе [Таблица методов модуля и функция инициализации](https://python-all.ru/3.5/extending/extending.html#methodtable)). И это также означает, что символы, которые *должны* быть доступны из других модулей расширения, должны экспортироваться иным способом.687688Python предоставляет специальный механизм для передачи информации уровня C (указателей) из одного модуля расширения в другой: капсулы (Capsules). Капсула – это тип данных Python, который хранит указатель (`void *`). Капсулы можно создавать и использовать только через их C API, но их можно передавать как любой другой объект Python. В частности, их можно присвоить имени в пространстве имён модуля расширения. Другие модули расширения могут затем импортировать этот модуль, получить значение этого имени и затем извлечь указатель из капсулы.689690Существует много способов использования Capsules для экспорта C API модуля расширения. Каждая функция может получить свой собственный Capsule, или все указатели C API могут храниться в массиве, адрес которого опубликован в Capsule. А различные задачи хранения и извлечения указателей могут быть распределены разными способами между модулем, предоставляющим код, и модулями-клиентами.691692Какой бы метод вы ни выбрали, важно правильно называть свои Capsule. Функция [`PyCapsule_New()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_New) принимает параметр name (`const char *`); разрешается передать имя *NULL*, но мы настоятельно рекомендуем указывать имя. Правильно названные Capsule обеспечивают определённую степень типобезопасности во время выполнения; нет практического способа отличить одну безымянную Capsule от другой.693694В частности, Capsules, используемые для предоставления C API, должны получать имена согласно следующему соглашению:695696```c697modulename.attributename698```699700Удобная функция [`PyCapsule_Import()`](https://python-all.ru/3.5/c-api/capsule.html#c.PyCapsule_Import) упрощает загрузку C API, предоставленного через Capsule, но только если имя Capsule соответствует этому соглашению. Такое поведение даёт пользователям C API высокую степень уверенности в том, что загруженный ими Capsule содержит правильный C API.701702Следующий пример демонстрирует подход, который возлагает большую часть ответственности на разработчика экспортирующего модуля, что подходит для часто используемых модулей библиотеки. Он хранит все указатели C API (в примере только один!) в массиве указателей `void`, который становится значением капсулы. Заголовочный файл, соответствующий модулю, предоставляет макрос, который импортирует модуль и получает его указатели C API; клиентским модулям остаётся только вызвать этот макрос перед обращением к C API.703704Экспортирующий модуль – это модификация модуля `spam` из раздела [Простой пример](https://python-all.ru/3.5/extending/extending.html#extending-simpleexample). Функция `spam.system()` не вызывает напрямую функцию библиотеки C `system()`, а вызывает функцию `PySpam_System()`, которая в реальности, конечно, делала бы что-то более сложное (например, добавляла бы «spam» к каждой команде). Эта функция `PySpam_System()` также экспортируется в другие модули расширения.705706Функция `PySpam_System()` – это обычная функция на C, объявленная `static`, как и всё остальное:707708```c709static int710PySpam_System(const char *command)711{712 return system(command);713}714```715716Функция `spam_system()` тривиально модифицирована:717718```c719static PyObject *720spam_system(PyObject *self, PyObject *args)721{722 const char *command;723 int sts;724725 if (!PyArg_ParseTuple(args, "s", &command))726 return NULL;727 sts = PySpam_System(command);728 return PyLong_FromLong(sts);729}730```731732В начале модуля, сразу после строки733734```c735#include "Python.h"736```737738необходимо добавить ещё две строки:739740```c741#define SPAM_MODULE742#include "spammodule.h"743```744745`#define` используется, чтобы сообщить заголовочному файлу, что он включается в экспортирующий модуль, а не в клиентский модуль. Наконец, функция инициализации модуля должна позаботиться об инициализации массива указателей C API:746747```c748PyMODINIT_FUNC749PyInit_spam(void)750{751 PyObject *m;752 static void *PySpam_API[PySpam_API_pointers];753 PyObject *c_api_object;754755 m = PyModule_Create(&spammodule);756 if (m == NULL)757 return NULL;758759 /* Инициализация массива указателей C API */760 PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;761762 /* Создание капсулы, содержащей адрес массива указателей API */763 c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);764765 if (c_api_object != NULL)766 PyModule_AddObject(m, "_C_API", c_api_object);767 return m;768}769```770771Обратите внимание, что `PySpam_API` объявлен `static`; иначе массив указателей исчезнет после завершения `PyInit_spam()`!772773Основная работа выполняется в заголовочном файле `spammodule.h`, который выглядит следующим образом:774775```c776#ifndef Py_SPAMMODULE_H777#define Py_SPAMMODULE_H778#ifdef __cplusplus779extern "C" {780#endif781782/* Заголовочный файл для spammodule */783784/* Функции C API */785#define PySpam_System_NUM 0786#define PySpam_System_RETURN int787#define PySpam_System_PROTO (const char *command)788789/* Общее количество указателей C API */790#define PySpam_API_pointers 1791792#ifdef SPAM_MODULE793/* Этот раздел используется при компиляции spammodule.c */794795static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;796797#else798/* Этот раздел используется в модулях, которые используют API spammodule */799800static void **PySpam_API;801802#define PySpam_System \803 (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])804805/* Возвращает -1 при ошибке, 0 при успехе.806 * PyCapsule_Import установит исключение, если произошла ошибка.807 */808static int809import_spam(void)810{811 PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);812 return (PySpam_API != NULL) ? 0 : -1;813}814815#endif816817#ifdef __cplusplus818}819#endif820821#endif /* !defined(Py_SPAMMODULE_H) */822```823824Всё, что нужно сделать клиентскому модулю для доступа к функции `PySpam_System()` – это вызвать функцию (точнее, макрос) `import_spam()` в своей функции инициализации:825826```c827PyMODINIT_FUNC828PyInit_client(void)829{830 PyObject *m;831832 m = PyModule_Create(&clientmodule);833 if (m == NULL)834 return NULL;835 if (import_spam() < 0)836 return NULL;837 /* дополнительная инициализация может быть выполнена здесь */838 return m;839}840```841842Главный недостаток этого подхода в том, что файл `spammodule.h` довольно сложен. Однако базовая структура одинакова для каждой экспортируемой функции, поэтому её достаточно изучить один раз.843844Наконец, стоит упомянуть, что Capsules предоставляют дополнительную функциональность, которая особенно полезна для выделения и освобождения памяти указателя, хранящегося в Capsule. Подробности описаны в справочном руководстве по Python/C API в разделе [Capsules](https://python-all.ru/3.5/c-api/capsule.html#capsules) и в реализации Capsules (файлы `Include/pycapsule.h` и `Objects/pycapsule.c` в дистрибутиве исходного кода Python).845846Сноски847848| [\[1\]](https://python-all.ru/3.5/extending/extending.html#id1) | Интерфейс для этой функции уже существует в стандартном модуле [`os`](https://python-all.ru/3.5/library/os.html#module-os) – он был выбран в качестве простого и наглядного примера. |849| --- | --- |850851| [\[2\]](https://python-all.ru/3.5/extending/extending.html#id2) | Метафора «заимствования» ссылки не совсем точна: владелец всё ещё имеет копию ссылки. |852| --- | --- |853854| [\[3\]](https://python-all.ru/3.5/extending/extending.html#id3) | Проверка того, что счётчик ссылок не меньше 1, **не работает** – сам счётчик ссылок может находиться в освобождённой памяти и поэтому может быть повторно использован для другого объекта! |855| --- | --- |856857| [\[4\]](https://python-all.ru/3.5/extending/extending.html#id4) | Эти гарантии не действуют при использовании «старого» стиля соглашения о вызовах – он всё ещё встречается в большом объёме существующего кода. |858| --- | --- |859