extending.md
1> **Источник:** https://python-all.ru/3.4/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.4/library/ctypes.html#module-ctypes) или библиотеку [cffi](https://python-all.ru/3.4/extending/extending.html), а не писать собственный код на C. Эти модули позволяют писать код на Python для взаимодействия с C-кодом и более переносимы между реализациями Python, чем написание и компиляция модуля расширения на C.1819## 1.1. Простой пример2021Создадим модуль расширения с именем `spam` (любимая еда фанатов Монти Пайтона...) и предположим, что мы хотим создать Python-интерфейс для C-функции `system()`. [\[1\]](https://python-all.ru/3.4/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` до включения любых стандартных заголовков.4142All user-visible symbols defined by `Python.h` have a prefix of `Py` or `PY`, except those defined in standard header files. For convenience, and since they are used extensively by the Python interpreter, `"Python.h"` includes a few standard header files: `<stdio.h>`, `<string.h>`, `<errno.h>`, and `<stdlib.h>`. If the latter header file does not exist on your system, it declares the functions `malloc()`, `free()` and `realloc()` directly.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.4/c-api/arg.html#c.PyArg_ParseTuple) в Python API проверяет типы аргументов и преобразует их в C-значения. Она использует строку-шаблон для определения требуемых типов аргументов, а также типов C-переменных, в которые будут сохранены преобразованные значения. Подробнее об этом позже.6566[`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) возвращает true (ненулевое значение), если все аргументы имеют правильный тип и их компоненты сохранены в переменные, адреса которых переданы. Она возвращает false (ноль), если передан недопустимый список аргументов. В последнем случае она также возбуждает соответствующее исключение, так что вызывающая функция может немедленно вернуть *NULL* (как мы видели в примере).6768## 1.2. Интермеццо: Ошибки и исключения6970Важное соглашение во всём интерпретаторе Python заключается в следующем: когда функция завершается неудачей, она должна установить условие исключения и вернуть значение ошибки (обычно указатель *NULL*). Исключения хранятся в статической глобальной переменной внутри интерпретатора; если эта переменная равна *NULL*, исключения не произошло. Вторая глобальная переменная хранит «связанное значение» исключения (второй аргумент [`raise`](https://python-all.ru/3.4/reference/simple_stmts.html#raise)). Третья переменная содержит трассировку стека, если ошибка возникла в Python-коде. Эти три переменные являются C-эквивалентами результата Python-функции [`sys.exc_info()`](https://python-all.ru/3.4/library/sys.html#sys.exc_info) (см. раздел о модуле [`sys`](https://python-all.ru/3.4/library/sys.html#module-sys) в справочнике по библиотеке Python). Важно знать о них, чтобы понимать, как передаются ошибки.7172Python API определяет ряд функций для установки различных типов исключений.7374Наиболее распространённая – [`PyErr_SetString()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_SetString). Её аргументы – объект исключения и C-строка. Объект исключения обычно является предопределённым объектом, например `PyExc_ZeroDivisionError`. C-строка указывает причину ошибки, преобразуется в строковый объект Python и сохраняется как «связанное значение» исключения.7576Ещё одна полезная функция – [`PyErr_SetFromErrno()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_SetFromErrno), которая принимает только аргумент исключения и формирует связанное значение путём проверки глобальной переменной `errno`. Наиболее общая функция – [`PyErr_SetObject()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_SetObject), принимающая два объектных аргумента: исключение и его связанное значение. Не нужно вызывать [`Py_INCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_INCREF) для объектов, передаваемых любой из этих функций.7778Можно проверить, было ли установлено исключение, не разрушая его, с помощью [`PyErr_Occurred()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_Occurred). Она возвращает текущий объект исключения или *NULL*, если исключения не было. Обычно нет необходимости вызывать [`PyErr_Occurred()`](https://python-all.ru/3.4/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.4/c-api/exceptions.html#c.PyErr_Clear). Единственный случай, когда C-код должен вызывать [`PyErr_Clear()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_Clear), – это если он не хочет передавать ошибку интерпретатору, а хочет обработать её полностью самостоятельно (возможно, попробовать что-то другое или сделать вид, что ничего не произошло).8586Каждый неудачный вызов `malloc()` должен быть преобразован в исключение – непосредственный вызывающий `malloc()` (или `realloc()`) должен вызвать [`PyErr_NoMemory()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_NoMemory) и сам вернуть признак ошибки. Все функции, создающие объекты (например, [`PyLong_FromLong()`](https://python-all.ru/3.4/c-api/long.html#c.PyLong_FromLong)), уже делают это, поэтому это замечание актуально только для тех, кто вызывает `malloc()` напрямую.8788Также обратите внимание, что, за важным исключением [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) и её родственников, функции, возвращающие целочисленный статус, обычно возвращают положительное значение или ноль при успехе и `-1` при неудаче, как системные вызовы Unix.8990И наконец, не забывайте очищать мусор (вызывая [`Py_XDECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_XDECREF) или [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF) для уже созданных объектов), когда возвращаете признак ошибки!9192Выбор, какое исключение возбуждать, полностью за вами. Существуют предварительно объявленные C-объекты, соответствующие всем встроенным исключениям Python, такие как `PyExc_ZeroDivisionError`, которые можно использовать напрямую. Разумеется, выбирать исключения следует разумно – не используйте `PyExc_TypeError` для обозначения того, что файл не удалось открыть (для этого, вероятно, подойдёт `PyExc_IOError`). Если что-то не так со списком аргументов, функция [`PyArg_ParseTuple()`](https://python-all.ru/3.4/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.4/c-api/exceptions.html#c.PyErr_NewException) может создать класс, базовым классом которого будет [`Exception`](https://python-all.ru/3.4/library/exceptions.html#Exception) (если только вместо *NULL* не передан другой класс), описанный в разделе [*Встроенные исключения*](https://python-all.ru/3.4/library/exceptions.html#bltin-exceptions).120121Также обратите внимание, что переменная `SpamError` сохраняет ссылку на вновь созданный класс исключения; это сделано намеренно! Поскольку исключение может быть удалено из модуля внешним кодом, необходима владеющая ссылка на класс, чтобы гарантировать, что он не будет отброшен, что привело бы к превращению `SpamError` в висячий указатель. Если он станет висячим указателем, C-код, возбуждающий исключение, может вызвать дамп памяти или другие непреднамеренные побочные эффекты.122123Использование `PyMODINIT_FUNC` в качестве типа возвращаемого функцией значения мы обсудим позднее в этом примере.124125Исключение `spam.error` можно возбудить в вашем модуле расширения с помощью вызова [`PyErr_SetString()`](https://python-all.ru/3.4/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.4/c-api/arg.html#c.PyArg_ParseTuple). В противном случае строковое значение аргумента копируется в локальную переменную `command`. Это присваивание указателя, и вы не должны изменять строку, на которую он указывает (поэтому в стандартном C переменная `command` должна быть правильно объявлена как `const char *command`).155156Следующий оператор – вызов Unix-функции `system()`, которой передаётся строка, полученная от [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple):157158```c159sts = system(command);160```161162Наша функция `spam.system()` должна вернуть значение `sts` как объект Python. Это делается с помощью функции [`PyLong_FromLong()`](https://python-all.ru/3.4/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.4/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.4/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.4/c-api/arg.html#c.PyArg_ParseTuple).194195При использовании только `METH_VARARGS` функция должна ожидать, что параметры уровня Python будут переданы в виде кортежа, пригодного для разбора с помощью [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple); дополнительная информация об этой функции приведена ниже.196197Бит [`METH_KEYWORDS`](https://python-all.ru/3.4/c-api/structures.html#METH_KEYWORDS) может быть установлен в третьем поле, если в функцию должны передаваться именованные аргументы. В этом случае C-функция должна принимать третий параметр `PyObject *`, который будет словарём ключевых слов. Используйте [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/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.4/c-api/module.html#c.PyModule_Create), которая возвращает объект модуля и вставляет объекты встроенных функций в только что созданный модуль на основе таблицы (массива структур [`PyMethodDef`](https://python-all.ru/3.4/c-api/structures.html#c.PyMethodDef)), находящейся в определении модуля. [`PyModule_Create()`](https://python-all.ru/3.4/c-api/module.html#c.PyModule_Create) возвращает указатель на создаваемый объект модуля. Она может аварийно завершиться с фатальной ошибкой при некоторых ошибках или вернуть *NULL*, если модуль не удалось инициализировать удовлетворительно. Функция init должна вернуть объект модуля своему вызывающему, чтобы затем он был вставлен в `sys.modules`.225226При встраивании Python функция `PyInit_spam()` не вызывается автоматически, если только нет записи в таблице `PyImport_Inittab`. Чтобы добавить модуль в таблицу инициализации, используйте [`PyImport_AppendInittab()`](https://python-all.ru/3.4/c-api/import.html#c.PyImport_AppendInittab), после чего, при необходимости, импортируйте модуль:227228```c229int230main(int argc, char *argv[])231{232 /* Добавить встроенный модуль перед Py_Initialize */233 PyImport_AppendInittab("spam", PyInit_spam);234235 /* Передать argv[0] интерпретатору Python */236 Py_SetProgramName(argv[0]);237238 /* Инициализировать интерпретатор Python. Обязательно. */239 Py_Initialize();240241 /* Опционально импортировать модуль; в качестве альтернативы,242 импорт может быть отложен до тех пор, пока встроенный скрипт243 не импортирует его. */244 PyImport_ImportModule("spam");245246 ...247```248249> **Примечание**250>251> Удаление записей из `sys.modules` или импорт скомпилированных модулей в несколько интерпретаторов в рамках одного процесса (или после `fork()` без последующего `exec()`) может создавать проблемы для некоторых модулей расширения. Авторам модулей расширения следует проявлять осторожность при инициализации внутренних структур данных.252253Более полный пример модуля включён в дистрибутив исходного кода Python как `Modules/xxmodule.c`. Этот файл можно использовать как шаблон или просто прочитать как пример.254255## 1.5. Компиляция и компоновка256257Прежде чем вы сможете использовать своё новое расширение, нужно сделать ещё две вещи: скомпилировать и скомпоновать его с системой Python. Если вы используете динамическую загрузку, детали могут зависеть от стиля динамической загрузки, применяемого в вашей системе; обратитесь к главам о сборке модулей расширения (глава [*Building C and C++ Extensions with distutils*](https://python-all.ru/3.4/extending/building.html#building)) и дополнительной информации, относящейся только к сборке в Windows (глава [*Building C and C++ Extensions on Windows*](https://python-all.ru/3.4/extending/windows.html#building-on-windows)) для получения дополнительных сведений.258259Если вы не можете использовать динамическую загрузку или хотите сделать свой модуль постоянной частью интерпретатора Python, вам придётся изменить конфигурацию и пересобрать интерпретатор. К счастью, в Unix это очень просто: просто поместите ваш файл (например, `spammodule.c`) в каталог `Modules/` распакованного дистрибутива исходного кода, добавьте строку в файл `Modules/Setup.local`, описывающую ваш файл:260261```c262spam spammodule.o263```264265и пересобрать интерпретатор, выполнив **make** в корневом каталоге. Вы также можете выполнить **make** в подкаталоге `Modules/`, но тогда сначала нужно пересобрать там `Makefile`, выполнив '**make** Makefile'. (Это необходимо делать каждый раз при изменении файла `Setup`.)266267Если ваш модуль требует дополнительных библиотек для компоновки, их также можно перечислить в строке конфигурационного файла, например:268269```c270spam spammodule.o -lX11271```272273## 1.6. Вызов функций Python из C274275До сих пор мы сосредотачивались на том, как сделать C-функции вызываемыми из Python. Обратное также полезно: вызов функций Python из C. Это особенно актуально для библиотек, поддерживающих так называемые «колбэки». Если C-интерфейс использует колбэки, то эквивалентный Python часто должен предоставить механизм колбэков программисту на Python; реализация потребует вызова функций Python-колбэков из C-колбэка. Можно представить и другие варианты использования.276277К счастью, интерпретатор Python легко вызывается рекурсивно, и существует стандартный интерфейс для вызова функции Python. (Я не буду углубляться в то, как вызвать парсер Python с конкретной строкой в качестве входных данных – если вам интересно, посмотрите на реализацию опции командной строки [*-c*](https://python-all.ru/3.4/using/cmdline.html#cmdoption-c) в `Modules/main.c` из исходного кода Python.)278279Вызов функции Python прост. Во-первых, программа Python должна каким-то образом передать вам объект функции Python. Вам следует предоставить функцию (или другой интерфейс) для этого. Когда эта функция вызывается, сохраните указатель на объект функции Python (не забудьте [`Py_INCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_INCREF) его!) в глобальной переменной – или где сочтёте нужным. Например, следующая функция может быть частью определения модуля:280281```c282static PyObject *my_callback = NULL;283284static PyObject *285my_set_callback(PyObject *dummy, PyObject *args)286{287 PyObject *result = NULL;288 PyObject *temp;289290 if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {291 if (!PyCallable_Check(temp)) {292 PyErr_SetString(PyExc_TypeError, "parameter must be callable");293 return NULL;294 }295 Py_XINCREF(temp); /* Добавить ссылку на новый колбэк */296 Py_XDECREF(my_callback); /* Освободить предыдущий колбэк */297 my_callback = temp; /* Запомнить новый колбэк */298 /* Шаблон для возврата None */299 Py_INCREF(Py_None);300 result = Py_None;301 }302 return result;303}304```305306Эта функция должна быть зарегистрирована в интерпретаторе с использованием флага [`METH_VARARGS`](https://python-all.ru/3.4/c-api/structures.html#METH_VARARGS); это описано в разделе [*The Module’s Method Table and Initialization Function*](https://python-all.ru/3.4/extending/extending.html#methodtable). Функция [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) и её аргументы описаны в разделе [*Extracting Parameters in Extension Functions*](https://python-all.ru/3.4/extending/extending.html#parsetuple).307308Макросы [`Py_XINCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_XINCREF) и [`Py_XDECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_XDECREF) увеличивают/уменьшают счётчик ссылок объекта и безопасны при наличии указателей *NULL* (но обратите внимание, что *temp* в данном контексте не будет *NULL*). Дополнительная информация о них – в разделе [*Reference Counts*](https://python-all.ru/3.4/extending/extending.html#refcounts).309310Затем, когда наступает время вызвать функцию, вызывается C-функция [`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject). Эта функция принимает два аргумента, оба – указатели на произвольные объекты Python: саму функцию Python и список аргументов. Список аргументов всегда должен быть объектом-кортежем, длина которого равна количеству аргументов. Чтобы вызвать функцию Python без аргументов, передайте NULL или пустой кортеж; чтобы вызвать с одним аргументом, передайте кортеж из одного элемента. [`Py_BuildValue()`](https://python-all.ru/3.4/c-api/arg.html#c.Py_BuildValue) возвращает кортеж, если его строка формата состоит из нуля или более кодов формата в круглых скобках. Например:311312```c313int arg;314PyObject *arglist;315PyObject *result;316...317arg = 123;318...319/* Вызов колбэка */320arglist = Py_BuildValue("(i)", arg);321result = PyObject_CallObject(my_callback, arglist);322Py_DECREF(arglist);323```324325[`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject) возвращает указатель на объект Python: это возвращаемое значение функции Python. [`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject) является «нейтральной по отношению к счётчику ссылок» в отношении своих аргументов. В примере был создан новый кортеж для использования в качестве списка аргументов, который сразу после вызова [`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject) был освобождён с помощью [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF).326327Возвращаемое значение [`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject) является «новым»: это либо совершенно новый объект, либо существующий объект, чей счётчик ссылок был увеличен. Поэтому, если вы не хотите сохранять его в глобальной переменной, вам следует каким-то образом [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF) результат, даже (и особенно!) если вы не заинтересованы в его значении.328329Однако перед этим важно проверить, не является ли возвращаемое значение *NULL*. Если это так, функция Python завершилась, возбудив исключение. Если C-код, вызвавший [`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject), сам вызван из Python, он должен теперь вернуть своему вызывающему из Python признак ошибки, чтобы интерпретатор мог напечатать трассировку стека или вызывающий код Python мог обработать исключение. Если это невозможно или нежелательно, исключение следует очистить, вызвав [`PyErr_Clear()`](https://python-all.ru/3.4/c-api/exceptions.html#c.PyErr_Clear). Например:330331```c332if (result == NULL)333 return NULL; /* Передать ошибку обратно */334...use result...335Py_DECREF(result);336```337338В зависимости от желаемого интерфейса к функции обратного вызова Python вам также может потребоваться предоставить список аргументов для [`PyObject_CallObject()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_CallObject). В некоторых случаях список аргументов также предоставляется программой Python через тот же интерфейс, который задал функцию обратного вызова. Затем его можно сохранить и использовать так же, как объект функции. В других случаях вам может потребоваться создать новый кортеж для передачи в качестве списка аргументов. Самый простой способ сделать это – вызвать [`Py_BuildValue()`](https://python-all.ru/3.4/c-api/arg.html#c.Py_BuildValue). Например, если вы хотите передать целочисленный код события, можно использовать следующий код:339340```c341PyObject *arglist;342...343arglist = Py_BuildValue("(l)", eventcode);344result = PyObject_CallObject(my_callback, arglist);345Py_DECREF(arglist);346if (result == NULL)347 return NULL; /* Передать ошибку обратно */348/* Здесь, возможно, используется результат */349Py_DECREF(result);350```351352Обратите внимание на размещение `Py_DECREF(arglist)` сразу после вызова, до проверки ошибок! Также обратите внимание, что строго говоря, этот код неполон: [`Py_BuildValue()`](https://python-all.ru/3.4/c-api/arg.html#c.Py_BuildValue) может исчерпать память, и это следует проверять.353354Вы также можете вызвать функцию с именованными аргументами, используя [`PyObject_Call()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_Call), которая поддерживает аргументы и именованные аргументы. Как и в приведённом выше примере, мы используем [`Py_BuildValue()`](https://python-all.ru/3.4/c-api/arg.html#c.Py_BuildValue) для создания словаря.355356```c357PyObject *dict;358...359dict = Py_BuildValue("{s:i}", "name", val);360result = PyObject_Call(my_callback, NULL, dict);361Py_DECREF(dict);362if (result == NULL)363 return NULL; /* Передать ошибку обратно */364/* Здесь, возможно, используется результат */365Py_DECREF(result);366```367368## 1.7. Извлечение параметров в функциях расширения369370Функция [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) объявляется следующим образом:371372```c373int PyArg_ParseTuple(PyObject *arg, const char *format, ...);374```375376Аргумент *arg* должен быть объектом кортежа, содержащим список аргументов, переданных из Python в C-функцию. Аргумент *format* должен быть строкой формата, чей синтаксис описан в разделе [*Разбор аргументов и построение значений*](https://python-all.ru/3.4/c-api/arg.html#arg-parsing) справочного руководства Python/C API. Остальные аргументы должны быть адресами переменных, чей тип определяется строкой формата.377378Обратите внимание: хотя [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple) проверяет, что аргументы Python имеют требуемые типы, он не может проверить правильность адресов C-переменных, переданных в вызов: если вы допустите там ошибки, ваш код, вероятно, аварийно завершится или, по крайней мере, перезапишет случайные биты в памяти. Так что будьте внимательны!379380Обратите внимание, что любые ссылки на объекты Python, передаваемые вызывающему, являются *заимствованными* ссылками; уменьшать их счётчик ссылок не следует.381382Несколько примеров вызовов:383384```c385#define PY_SSIZE_T_CLEAN /* Сделать так, чтобы "s#" использовало Py_ssize_t, а не int. */386#include <Python.h>387```388389```c390int ok;391int i, j;392long k, l;393const char *s;394Py_ssize_t size;395396ok = PyArg_ParseTuple(args, ""); /* Без аргументов */397 /* Вызов Python: f() */398```399400```c401ok = PyArg_ParseTuple(args, "s", &s); /* Строка */402 /* Возможный вызов Python: f('whoops!') */403```404405```c406ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Два длинных целых и строка */407 /* Возможный вызов Python: f(1, 2, 'three') */408```409410```c411ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);412 /* Пара int и строка, размер которой также возвращается */413 /* Возможный вызов Python: f((1, 2), 'three') */414```415416```c417{418 const char *file;419 const char *mode = "r";420 int bufsize = 0;421 ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);422 /* Строка и, опционально, ещё одна строка и целое число */423 /* Возможные вызовы Python:424 f('spam')425 f('spam', 'w')426 f('spam', 'wb', 100000) */427}428```429430```c431{432 int left, top, right, bottom, h, v;433 ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",434 &left, &top, &right, &bottom, &h, &v);435 /* Прямоугольник и точка */436 /* Возможный вызов Python:437 f(((0, 0), (400, 300)), (10, 10)) */438}439```440441```c442{443 Py_complex c;444 ok = PyArg_ParseTuple(args, "D:myfunction", &c);445 /* комплексное число, также предоставляющее имя функции для сообщений об ошибках */446 /* Возможный вызов Python: myfunction(1+2j) */447}448```449450## 1.8. Именованные параметры для функций расширения451452Функция [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) объявляется следующим образом:453454```c455int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,456 const char *format, char *kwlist[], ...);457```458459Параметры *arg* и *format* идентичны соответствующим параметрам функции [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple). Параметр *kwdict* – это словарь ключевых слов, полученный в качестве третьего параметра от среды выполнения Python. Параметр *kwlist* – это завершаемый *NULL* список строк, идентифицирующих параметры; имена сопоставляются с информацией о типах из *format* слева направо. В случае успеха [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) возвращает true, в противном случае возвращает false и возбуждает соответствующее исключение.460461> **Примечание**462>463> Вложенные кортежи не могут быть разобраны при использовании именованных аргументов! Переданные именованные параметры, отсутствующие в *kwlist*, приведут к возбуждению исключения [`TypeError`](https://python-all.ru/3.4/library/exceptions.html#TypeError).464465Вот пример модуля, использующего ключевые слова, основанный на примере Джеффа Филбрика ([philbrick@hks.com](https://python-all.ru/3.4/extending/extending.html)):466467```c468#include "Python.h"469470static PyObject *471keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)472{473 int voltage;474 char *state = "a stiff";475 char *action = "voom";476 char *type = "Norwegian Blue";477478 static char *kwlist[] = {"voltage", "state", "action", "type", NULL};479480 if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,481 &voltage, &state, &action, &type))482 return NULL;483484 printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",485 action, voltage);486 printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);487488 Py_RETURN_NONE;489}490491static PyMethodDef keywdarg_methods[] = {492 /* Приведение функции необходимо, поскольку значения PyCFunction493 * принимают только два параметра PyObject*, а keywdarg_parrot() принимает494 * три.495 */496 {"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,497 "Print a lovely skit to standard output."},498 {NULL, NULL, 0, NULL} /* сторожевое значение */499};500501static struct PyModuleDef keywdargmodule = {502 PyModuleDef_HEAD_INIT,503 "keywdarg",504 NULL,505 -1,506 keywdarg_methods507};508509PyMODINIT_FUNC510PyInit_keywdarg(void)511{512 return PyModule_Create(&keywdargmodule);513}514```515516## 1.9. Построение произвольных значений517518Эта функция является аналогом [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple). Она объявляется следующим образом:519520```c521PyObject *Py_BuildValue(const char *format, ...);522```523524Она распознаёт набор единиц формата, аналогичный распознаваемому [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple), но аргументы (которые являются входными для функции, а не выходными) не должны быть указателями, а только значениями. Она возвращает новый объект Python, пригодный для возврата из C-функции, вызываемой из Python.525526Одно отличие от [`PyArg_ParseTuple()`](https://python-all.ru/3.4/c-api/arg.html#c.PyArg_ParseTuple): в то время как последняя требует, чтобы её первый аргумент был кортежем (поскольку списки аргументов Python внутренне всегда представлены как кортежи), [`Py_BuildValue()`](https://python-all.ru/3.4/c-api/arg.html#c.Py_BuildValue) не всегда строит кортеж. Она строит кортеж только в том случае, если её строка формата содержит две или более единиц формата. Если строка формата пуста, она возвращает `None`; если она содержит ровно одну единицу формата, она возвращает объект, описываемый этой единицей формата. Чтобы заставить её вернуть кортеж размером 0 или 1, заключите строку формата в круглые скобки.527528Примеры (слева вызов, справа результирующее значение Python):529530```c531Py_BuildValue("") None532Py_BuildValue("i", 123) 123533Py_BuildValue("iii", 123, 456, 789) (123, 456, 789)534Py_BuildValue("s", "hello") 'hello'535Py_BuildValue("y", "hello") b'hello'536Py_BuildValue("ss", "hello", "world") ('hello', 'world')537Py_BuildValue("s#", "hello", 4) 'hell'538Py_BuildValue("y#", "hello", 4) b'hell'539Py_BuildValue("()") ()540Py_BuildValue("(i)", 123) (123,)541Py_BuildValue("(ii)", 123, 456) (123, 456)542Py_BuildValue("(i,i)", 123, 456) (123, 456)543Py_BuildValue("[i,i]", 123, 456) [123, 456]544Py_BuildValue("{s:i,s:i}",545 "abc", 123, "def", 456) {'abc': 123, 'def': 456}546Py_BuildValue("((ii)(ii)) (ii)",547 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))548```549550## 1.10. Подсчёт ссылок551552В таких языках, как C или C++, программист отвечает за динамическое выделение и освобождение памяти в куче. В C это делается с помощью функций `malloc()` и `free()`. В C++ операторы `new` и `delete` используются с тем же значением, и мы ограничим дальнейшее обсуждение случаем C.553554Каждый блок памяти, выделенный с помощью `malloc()`, должен быть возвращён в пул доступной памяти ровно одним вызовом `free()`. Важно вызывать `free()` в нужный момент. Если адрес блока забыт, а `free()` для него не вызывается, занимаемая им память не может быть повторно использована до завершения программы. Это называется *утечкой памяти*. С другой стороны, если программа вызывает `free()` для блока, а затем продолжает его использовать, это создаёт конфликт с повторным использованием блока через другой вызов `malloc()`. Это называется *использованием освобождённой памяти*. Это имеет те же плохие последствия, что и обращение к неинициализированным данным – дампы ядра, неверные результаты, загадочные сбои.555556Распространённые причины утечек памяти – нестандартные пути выполнения кода. Например, функция может выделить блок памяти, выполнить некоторые вычисления, а затем снова освободить блок. Теперь изменение требований к функции может добавить в вычисление проверку, которая обнаруживает ошибочную ситуацию и может привести к преждевременному возврату из функции. Легко забыть освободить выделенный блок памяти при таком преждевременном выходе, особенно если он был добавлен в код позднее. Такие утечки, будучи однажды внесёнными, часто остаются незамеченными долгое время: ошибочный выход происходит лишь в небольшой доле всех вызовов, а на большинстве современных машин достаточно виртуальной памяти, поэтому утечка становится заметна только в долго работающем процессе, который часто использует функцию с утечкой. Следовательно, важно предотвращать утечки, используя соглашение о кодировании или стратегию, которая минимизирует такого рода ошибки.557558Поскольку Python активно использует `malloc()` и `free()`, ему нужна стратегия для предотвращения как утечек памяти, так и использования освобождённой памяти. Выбранный метод называется *подсчётом ссылок*. Принцип прост: каждый объект содержит счётчик, который увеличивается, когда ссылка на объект сохраняется где-либо, и уменьшается, когда ссылка на него удаляется. Когда счётчик достигает нуля, последняя ссылка на объект была удалена и объект освобождается.559560Альтернативная стратегия называется *автоматической сборкой мусора*. (Иногда подсчёт ссылок также относят к стратегиям сборки мусора, поэтому я использую термин «автоматическая» для различия.) Большое преимущество автоматической сборки мусора в том, что пользователю не нужно явно вызывать `free()`. (Другое заявленное преимущество – повышение скорости или уменьшение использования памяти – однако это не твёрдый факт.) Недостаток в том, что для C не существует по-настоящему переносимого автоматического сборщика мусора, в то время как подсчёт ссылок может быть реализован переносимо (при условии, что функции `malloc()` и `free()` доступны – что гарантирует стандарт C). Возможно, когда-нибудь для C появится достаточно переносимый автоматический сборщик мусора. А пока приходится мириться с подсчётом ссылок.561562Хотя Python использует традиционную реализацию подсчёта ссылок, он также предлагает детектор циклов, который обнаруживает циклические ссылки. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок; они являются слабым местом сборки мусора, реализованной только с помощью подсчёта ссылок. Циклические ссылки состоят из объектов, которые содержат (возможно, косвенные) ссылки на самих себя, так что каждый объект в цикле имеет ненулевой счётчик ссылок. Типичные реализации подсчёта ссылок не могут освободить память, принадлежащую объектам в циклической ссылке или на которые есть ссылки из объектов в цикле, даже если на сам цикл больше нет ссылок.563564Детектор циклов способен обнаруживать циклический мусор и перерабатывать его. Модуль [`gc`](https://python-all.ru/3.4/library/gc.html#module-gc) предоставляет способ запуска детектора (функция [`collect()`](https://python-all.ru/3.4/library/gc.html#gc.collect)), а также конфигурационные интерфейсы и возможность отключения детектора во время выполнения. Детектор циклов считается опциональным компонентом; хотя он включён по умолчанию, его можно отключить при сборке с помощью опции *--without-cycle-gc* к скрипту **configure** на платформах Unix (включая Mac OS X). Если детектор циклов отключён таким образом, модуль [`gc`](https://python-all.ru/3.4/library/gc.html#module-gc) будет не доступен.565566### 1.10.1. Подсчёт ссылок в Python567568Существуют два макроса: `Py_INCREF(x)` и `Py_DECREF(x)`, которые управляют увеличением и уменьшением счётчика ссылок. [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF) также освобождает объект, когда счётчик достигает нуля. Для гибкости он не вызывает `free()` напрямую – вместо этого он делает вызов через указатель на функцию в *объекте-типе* объекта. Для этой цели (и других) каждый объект также содержит указатель на свой объект-тип.569570Остаётся главный вопрос: когда использовать `Py_INCREF(x)` и `Py_DECREF(x)`? Сначала введём некоторые термины. Никто не «владеет» объектом; однако можно *владеть ссылкой* на объект. Счётчик ссылок объекта теперь определяется как количество владельческих ссылок на него. Владелец ссылки отвечает за вызов [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF), когда ссылка больше не нужна. Владение ссылкой может быть передано. Есть три способа избавиться от владельческой ссылки: передать её, сохранить или вызвать [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF). Забыть избавиться от владельческой ссылки приводит к утечке памяти.571572Также можно *заимствовать* [\[2\]](https://python-all.ru/3.4/extending/extending.html#id6) ссылку на объект. Заимствующий ссылку не должен вызывать [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF). Заимствующий не должен удерживать объект дольше, чем владелец, у которого он был заимствован. Использование заимствованной ссылки после того, как владелец избавился от неё, рискует привести к использованию освобождённой памяти и должно быть полностью избегаемо. [\[3\]](https://python-all.ru/3.4/extending/extending.html#id7)573574Преимущество заимствования ссылки перед владением в том, что не нужно заботиться о распоряжении ссылкой на всех возможных путях выполнения кода – иными словами, с заимствованной ссылкой нет риска утечки при преждевременном выходе. Недостаток заимствования по сравнению с владением в том, что существуют некоторые тонкие ситуации, когда в, казалось бы, корректном коде заимствованная ссылка может быть использована после того, как владелец, у которого она была заимствована, фактически распорядился ею.575576Заимствованную ссылку можно превратить во владельческую, вызвав [`Py_INCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_INCREF). Это не влияет на статус владельца, у которого ссылка была заимствована – создаётся новая владельческая ссылка и возлагаются полные обязанности владельца (новый владелец должен правильно избавиться от ссылки, как и предыдущий владелец).577578### 1.10.2. Правила владения579580Всякий раз, когда ссылка на объект передаётся в функцию или из неё, является частью спецификации интерфейса функции, передаётся ли владение вместе со ссылкой или нет.581582Большинство функций, возвращающих ссылку на объект, передают и владение этой ссылкой. В частности, все функции, чья задача – создавать новый объект, такие как [`PyLong_FromLong()`](https://python-all.ru/3.4/c-api/long.html#c.PyLong_FromLong) и [`Py_BuildValue()`](https://python-all.ru/3.4/c-api/arg.html#c.Py_BuildValue), передают владение получателю. Даже если объект не является новым, вы всё равно получаете владение новой ссылкой на этот объект. Например, [`PyLong_FromLong()`](https://python-all.ru/3.4/c-api/long.html#c.PyLong_FromLong) поддерживает кеш популярных значений и может вернуть ссылку на кешированный элемент.583584Многие функции, извлекающие объекты из других объектов, также передают владение вместе со ссылкой, например [`PyObject_GetAttrString()`](https://python-all.ru/3.4/c-api/object.html#c.PyObject_GetAttrString). Однако картина менее ясна, поскольку несколько распространённых подпрограмм являются исключениями: [`PyTuple_GetItem()`](https://python-all.ru/3.4/c-api/tuple.html#c.PyTuple_GetItem), [`PyList_GetItem()`](https://python-all.ru/3.4/c-api/list.html#c.PyList_GetItem), [`PyDict_GetItem()`](https://python-all.ru/3.4/c-api/dict.html#c.PyDict_GetItem) и [`PyDict_GetItemString()`](https://python-all.ru/3.4/c-api/dict.html#c.PyDict_GetItemString) – все они возвращают ссылки, которые заимствуются из кортежа, списка или словаря.585586Функция [`PyImport_AddModule()`](https://python-all.ru/3.4/c-api/import.html#c.PyImport_AddModule) также возвращает заимствованную ссылку, даже хотя она может фактически создать возвращаемый объект: это возможно, потому что владельческая ссылка на объект хранится в `sys.modules`.587588Когда вы передаёте ссылку на объект другой функции, обычно функция заимствует эту ссылку у вас – если ей нужно сохранить её, она использует [`Py_INCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_INCREF), чтобы стать независимым владельцем. Из этого правила есть ровно два важных исключения: [`PyTuple_SetItem()`](https://python-all.ru/3.4/c-api/tuple.html#c.PyTuple_SetItem) и [`PyList_SetItem()`](https://python-all.ru/3.4/c-api/list.html#c.PyList_SetItem). Эти функции принимают на себя владение переданным элементом – даже в случае неудачи! (Обратите внимание, что [`PyDict_SetItem()`](https://python-all.ru/3.4/c-api/dict.html#c.PyDict_SetItem) и подобные не принимают владение – они «нормальные».)589590Когда C-функция вызывается из Python, она заимствует ссылки на свои аргументы у вызывающего. Вызывающий владеет ссылкой на объект, поэтому время жизни заимствованной ссылки гарантировано до возврата функции. Только когда такая заимствованная ссылка должна быть сохранена или передана, она должна быть превращена во владельческую ссылку вызовом [`Py_INCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_INCREF).591592Ссылка на объект, возвращаемая из C-функции, вызванной из Python, должна быть собственной – владение передаётся от функции её вызывающей стороне.593594### 1.10.3. Тонкий лёд595596Есть несколько ситуаций, когда безобидное, на первый взгляд, использование заимствованной ссылки может привести к проблемам. Все они связаны с неявными вызовами интерпретатора, которые могут заставить владельца ссылки освободить её.597598Первый и самый важный случай, о котором следует знать, – это использование [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF) для не связанного объекта во время заимствования ссылки на элемент списка. Например:599600```c601void602bug(PyObject *list)603{604 PyObject *item = PyList_GetItem(list, 0);605606 PyList_SetItem(list, 1, PyLong_FromLong(0L));607 PyObject_Print(item, stdout, 0); /* ОШИБКА! */608}609```610611Эта функция сначала заимствует ссылку на `list[0]`, затем заменяет `list[1]` значением `0` и наконец печатает заимствованную ссылку. Выглядит безобидно, не так ли? Но это не так!612613Давайте проследим поток управления в [`PyList_SetItem()`](https://python-all.ru/3.4/c-api/list.html#c.PyList_SetItem). Список владеет ссылками на все свои элементы, поэтому когда элемент 1 заменяется, ему нужно избавиться от исходного элемента 1. Теперь предположим, что исходный элемент 1 был экземпляром пользовательского класса, и далее предположим, что класс определяет метод [`__del__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__del__). Если этот экземпляр класса имеет счётчик ссылок 1, его удаление вызовет его метод [`__del__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__del__).614615Поскольку метод [`__del__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__del__) написан на Python, он может выполнять произвольный код Python. Может ли он сделать что-то, чтобы аннулировать ссылку на `item` в `bug()`? Ещё как! Предполагая, что список, переданный в `bug()`, доступен методу [`__del__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__del__), он может выполнить оператор вроде `del list[0]`, и если это была последняя ссылка на этот объект, он освободит связанную с ним память, тем самым аннулируя `item`.616617Решение, если знать источник проблемы, простое: временно увеличить счётчик ссылок. Правильная версия функции выглядит так:618619```c620void621no_bug(PyObject *list)622{623 PyObject *item = PyList_GetItem(list, 0);624625 Py_INCREF(item);626 PyList_SetItem(list, 1, PyLong_FromLong(0L));627 PyObject_Print(item, stdout, 0);628 Py_DECREF(item);629}630```631632Это реальная история. В старой версии Python были варианты этой ошибки, и кто-то потратил значительное время в отладчике C, чтобы выяснить, почему его методы [`__del__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__del__) не срабатывали...633634Вторая проблема с заимствованной ссылкой – это вариант, связанный с потоками. Обычно несколько потоков в интерпретаторе Python не мешают друг другу, потому что существует глобальная блокировка, защищающая всё пространство объектов Python. Однако можно временно освободить эту блокировку с помощью макроса [`Py_BEGIN_ALLOW_THREADS`](https://python-all.ru/3.4/c-api/init.html#c.Py_BEGIN_ALLOW_THREADS), а затем снова захватить её с помощью [`Py_END_ALLOW_THREADS`](https://python-all.ru/3.4/c-api/init.html#c.Py_END_ALLOW_THREADS). Это обычно делается вокруг блокирующих операций ввода-вывода, чтобы другие потоки могли использовать процессор, пока ожидается завершение ввода-вывода. Очевидно, следующая функция имеет ту же проблему, что и предыдущая:635636```c637void638bug(PyObject *list)639{640 PyObject *item = PyList_GetItem(list, 0);641 Py_BEGIN_ALLOW_THREADS642 ...some blocking I/O call...643 Py_END_ALLOW_THREADS644 PyObject_Print(item, stdout, 0); /* ОШИБКА! */645}646```647648### 1.10.4. NULL-указатели649650В общем, функции, принимающие ссылки на объекты в качестве аргументов, не ожидают, что им будут передавать указатели *NULL*, и при такой передаче может произойти аварийное завершение (дамп ядра) или возникнуть последующие сбои. Функции, возвращающие ссылки на объекты, обычно возвращают *NULL* только для указания на то, что произошло исключение. Причина, по которой не проверяются аргументы на *NULL*, заключается в том, что функции часто передают полученные объекты другим функциям – если бы каждая функция проверяла *NULL*, было бы много избыточных проверок и код работал бы медленнее.651652Лучше проверять *NULL* только «у источника»: когда указатель, который может быть *NULL*, получен, например, от `malloc()` или от функции, которая может вызвать исключение.653654Макросы [`Py_INCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_INCREF) и [`Py_DECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_DECREF) не проверяют *NULL* указатели – однако их варианты [`Py_XINCREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_XINCREF) и [`Py_XDECREF()`](https://python-all.ru/3.4/c-api/refcounting.html#c.Py_XDECREF) проверяют.655656Макросы для проверки конкретного типа объекта (`Pytype_Check()`) не проверяют *NULL*-указатели – опять же, существует много кода, который вызывает несколько таких макросов подряд, чтобы проверить объект на соответствие различным ожидаемым типам, и это привело бы к избыточным проверкам. Не существует вариантов с проверкой *NULL*.657658Механизм вызова C-функций гарантирует, что список аргументов, передаваемый C-функциям (`args` в примерах), никогда не является *NULL* – на самом деле он гарантирует, что это всегда кортеж. [\[4\]](https://python-all.ru/3.4/extending/extending.html#id8)659660Серьёзной ошибкой является допустить, чтобы указатель *NULL* «попал» к пользователю Python.661662## 1.11. Написание расширений на C++663664Можно писать модули расширения на C++. Существуют некоторые ограничения. Если главная программа (интерпретатор Python) компилируется и компонуется C-компилятором, глобальные или статические объекты с конструкторами использоваться не могут. Это не проблема, если главная программа компонуется C++-компилятором. Функции, которые будут вызываться интерпретатором Python (в частности, функции инициализации модулей), должны быть объявлены с использованием `extern "C"`. Нет необходимости заключать заголовочные файлы Python в `extern "C" {...}` – они уже используют эту форму, если определён символ `__cplusplus` (все современные C++-компиляторы определяют этот символ).665666## 1.12. Предоставление C API для модуля расширения667668Многие модули расширения просто предоставляют новые функции и типы для использования из Python, но иногда код в модуле расширения может быть полезен для других модулей расширения. Например, модуль расширения может реализовать тип «коллекция», который работает как список без порядка. Подобно тому, как стандартный тип списка Python имеет C API, позволяющий модулям расширения создавать списки и управлять ими, этот новый тип коллекции должен иметь набор C-функций для прямого управления из других модулей расширения.669670На первый взгляд это кажется простым: просто написать функции (не объявляя их `static`, конечно), предоставить соответствующий заголовочный файл и задокументировать C API. И на самом деле это сработало бы, если бы все модули расширения всегда были статически скомпонованы с интерпретатором Python. Однако когда модули используются как разделяемые библиотеки, символы, определённые в одном модуле, могут быть невидимы для другого модуля. Детали видимости зависят от операционной системы; некоторые системы используют одно глобальное пространство имён для интерпретатора Python и всех модулей расширения (например, Windows), тогда как другие требуют явного списка импортируемых символов на этапе компоновки модуля (например, AIX) или предлагают выбор различных стратегий (большинство Unices). И даже если символы глобально видимы, модуль, функции которого требуется вызвать, возможно, ещё не загружен!671672Таким образом, переносимость требует не делать никаких предположений о видимости символов. Это означает, что все символы в модулях расширения должны быть объявлены как `static`, за исключением функции инициализации модуля, чтобы избежать конфликтов имён с другими модулями расширения (как обсуждалось в разделе [*The Module’s Method Table and Initialization Function*](https://python-all.ru/3.4/extending/extending.html#methodtable)). И это означает, что символы, которые *должны* быть доступны из других модулей расширения, должны экспортироваться иным способом.673674Python предоставляет специальный механизм для передачи информации на уровне C (указателей) из одного модуля расширения в другой: Capsules. Capsule – это тип данных Python, который хранит указатель (`void *`). Capsules могут быть созданы и доступны только через их C API, но их можно передавать как любой другой объект Python. В частности, их можно присвоить имени в пространстве имён модуля расширения. Другие модули расширения могут затем импортировать этот модуль, получить значение этого имени, а затем извлечь указатель из Capsule.675676Существует много способов использования Capsules для экспорта C API модуля расширения. Каждая функция может получить свой собственный Capsule, или все указатели C API могут храниться в массиве, адрес которого опубликован в Capsule. А различные задачи хранения и извлечения указателей могут быть распределены разными способами между модулем, предоставляющим код, и модулями-клиентами.677678Какой бы метод вы ни выбрали, важно правильно называть свои Capsules. Функция [`PyCapsule_New()`](https://python-all.ru/3.4/c-api/capsule.html#c.PyCapsule_New) принимает параметр name (`const char *`); разрешается передать *NULL* в качестве имени, но мы настоятельно рекомендуем указывать имя. Правильно названные Capsules обеспечивают некоторую степень типобезопасности во время выполнения; нет возможного способа отличить один безымянный Capsule от другого.679680В частности, Capsules, используемые для предоставления C API, должны получать имена согласно следующему соглашению:681682```c683modulename.attributename684```685686Удобная функция [`PyCapsule_Import()`](https://python-all.ru/3.4/c-api/capsule.html#c.PyCapsule_Import) упрощает загрузку C API, предоставляемого через Capsule, но только если имя Capsule соответствует этому соглашению. Такое поведение даёт пользователям C API высокую степень уверенности в том, что загружаемый ими Capsule содержит правильный C API.687688Следующий пример демонстрирует подход, при котором основная нагрузка ложится на разработчика экспортирующего модуля, что подходит для часто используемых библиотечных модулей. Он хранит все указатели C API (в примере только один!) в массиве указателей `void`, который становится значением Capsule. Заголовочный файл, соответствующий модулю, предоставляет макрос, который заботится об импорте модуля и извлечении его указателей C API; модулям-клиентам нужно только вызвать этот макрос перед доступом к C API.689690Экспортирующий модуль представляет собой модификацию модуля `spam` из раздела [*A Simple Example*](https://python-all.ru/3.4/extending/extending.html#extending-simpleexample). Функция `spam.system()` не вызывает напрямую библиотечную C-функцию `system()`, а вызывает функцию `PySpam_System()`, которая, конечно, в реальности делает нечто более сложное (например, добавляет «spam» к каждой команде). Эта функция `PySpam_System()` также экспортируется в другие модули расширения.691692Функция `PySpam_System()` – это обычная C-функция, объявленная как `static`, как и всё остальное:693694```c695static int696PySpam_System(const char *command)697{698 return system(command);699}700```701702Функция `spam_system()` тривиально изменена:703704```c705static PyObject *706spam_system(PyObject *self, PyObject *args)707{708 const char *command;709 int sts;710711 if (!PyArg_ParseTuple(args, "s", &command))712 return NULL;713 sts = PySpam_System(command);714 return PyLong_FromLong(sts);715}716```717718В начале модуля, сразу после строки719720```c721#include "Python.h"722```723724необходимо добавить ещё две строки:725726```c727#define SPAM_MODULE728#include "spammodule.h"729```730731`#define` используется, чтобы сообщить заголовочному файлу, что он включается в экспортирующий модуль, а не в клиентский. Наконец, функция инициализации модуля должна позаботиться об инициализации массива указателей C API:732733```c734PyMODINIT_FUNC735PyInit_spam(void)736{737 PyObject *m;738 static void *PySpam_API[PySpam_API_pointers];739 PyObject *c_api_object;740741 m = PyModule_Create(&spammodule);742 if (m == NULL)743 return NULL;744745 /* Инициализация массива указателей C API */746 PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;747748 /* Создание капсулы, содержащей адрес массива указателей API */749 c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);750751 if (c_api_object != NULL)752 PyModule_AddObject(m, "_C_API", c_api_object);753 return m;754}755```756757Обратите внимание, что `PySpam_API` объявлен `static`; иначе массив указателей исчезнет при завершении `PyInit_spam()`!758759Основная часть работы выполняется в заголовочном файле `spammodule.h`, который выглядит следующим образом:760761```c762#ifndef Py_SPAMMODULE_H763#define Py_SPAMMODULE_H764#ifdef __cplusplus765extern "C" {766#endif767768/* Заголовочный файл для spammodule */769770/* Функции C API */771#define PySpam_System_NUM 0772#define PySpam_System_RETURN int773#define PySpam_System_PROTO (const char *command)774775/* Общее количество указателей C API */776#define PySpam_API_pointers 1777778#ifdef SPAM_MODULE779/* Этот раздел используется при компиляции spammodule.c */780781static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;782783#else784/* Этот раздел используется в модулях, которые используют API spammodule */785786static void **PySpam_API;787788#define PySpam_System \789 (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])790791/* Возвращает -1 при ошибке, 0 при успехе.792 * PyCapsule_Import установит исключение, если произошла ошибка.793 */794static int795import_spam(void)796{797 PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);798 return (PySpam_API != NULL) ? 0 : -1;799}800801#endif802803#ifdef __cplusplus804}805#endif806807#endif /* !defined(Py_SPAMMODULE_H) */808```809810Всё, что нужно сделать клиентскому модулю для получения доступа к функции `PySpam_System()` – это вызвать функцию (или, точнее, макрос) `import_spam()` в его функции инициализации:811812```c813PyMODINIT_FUNC814PyInit_client(void)815{816 PyObject *m;817818 m = PyModule_Create(&clientmodule);819 if (m == NULL)820 return NULL;821 if (import_spam() < 0)822 return NULL;823 /* дополнительная инициализация может быть выполнена здесь */824 return m;825}826```827828Основной недостаток этого подхода в том, что файл `spammodule.h` является довольно сложным. Однако базовая структура одинакова для каждой экспортируемой функции, поэтому её нужно освоить лишь один раз.829830Наконец, следует упомянуть, что капсулы предлагают дополнительную функциональность, которая особенно полезна для выделения и освобождения памяти указателя, хранящегося в капсуле. Подробности описаны в справочном руководстве по Python/C API в разделе [*Capsules*](https://python-all.ru/3.4/c-api/capsule.html#capsules) и в реализации капсул (файлы `Include/pycapsule.h` и `Objects/pycapsule.c` в дистрибутиве исходного кода Python).831832Сноски833834| [\[1\]](https://python-all.ru/3.4/extending/extending.html#id1) | Интерфейс для этой функции уже существует в стандартном модуле [`os`](https://python-all.ru/3.4/library/os.html#module-os) – он был выбран как простой и понятный пример. |835| --- | --- |836837| [\[2\]](https://python-all.ru/3.4/extending/extending.html#id2) | Метафора «заимствования» ссылки не совсем точна: владелец всё ещё имеет копию ссылки. |838| --- | --- |839840| [\[3\]](https://python-all.ru/3.4/extending/extending.html#id3) | Проверка того, что счётчик ссылок не меньше 1, **не работает** – сам счётчик ссылок может находиться в освобождённой памяти и поэтому может быть повторно использован для другого объекта! |841| --- | --- |842843| [\[4\]](https://python-all.ru/3.4/extending/extending.html#id4) | Эти гарантии не действуют при использовании «старого» стиля соглашения о вызовах – он всё ещё встречается в большом объёме существующего кода. |844| --- | --- |845