extending.md · Markdown – Документация Python
Документация Python неофициальный перевод

extending.md

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

1> **Источник:** https://python-all.ru/3.16/extending/extending.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Использование C API: Различные темы89[Учебное пособие](https://python-all.ru/3.16/extending/first-extension-module.html#first-extension-module) позволило создать модуль расширения C API, но многие вопросы остались нераскрытыми. В этом документе рассматриваются несколько концепций, необходимых для написания более сложных расширений.1011## Ошибки и исключения1213Важное соглашение во всём интерпретаторе Python заключается в следующем: когда функция завершается ошибкой, она должна установить условие исключения и вернуть значение ошибки (обычно `-1` или указатель `NULL`). Информация об исключении хранится в трёх элементах состояния потока интерпретатора. Если исключения нет, они равны `NULL`. В противном случае они являются C-эквивалентами элементов кортежа Python, возвращаемого функцией [`sys.exc_info()`](https://python-all.ru/3.16/library/sys.html#sys.exc_info). Это тип исключения, экземпляр исключения и объект traceback. Важно знать о них, чтобы понимать, как передаются ошибки.1415Python API определяет ряд функций для установки различных типов исключений.1617Самая распространённая из них – [`PyErr_SetString()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_SetString). Её аргументы – объект исключения и C-строка. Объект исключения обычно является предопределённым объектом, таким как [`PyExc_ZeroDivisionError`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyExc_ZeroDivisionError). C-строка указывает причину ошибки и преобразуется в строковый объект Python, который сохраняется как «связанное значение» исключения.1819Другая полезная функция – [`PyErr_SetFromErrno()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_SetFromErrno), которая принимает только аргумент-исключение и формирует связанное значение, проверяя глобальную переменную `errno`. Самая общая функция – [`PyErr_SetObject()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_SetObject), принимающая два объектных аргумента: исключение и его связанное значение. Не требуется [`Py_INCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_INCREF) объекты, переданные любой из этих функций.2021Можно неразрушающе проверить, было ли установлено исключение, с помощью [`PyErr_Occurred()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_Occurred). Она возвращает текущий объект исключения или `NULL`, если исключения не возникло. Обычно не нужно вызывать `PyErr_Occurred()`, чтобы узнать, произошла ли ошибка при вызове функции, так как об этом можно судить по возвращаемому значению.2223Когда функция *f*, вызывающая другую функцию *g*, обнаруживает, что последняя завершилась ошибкой, *f* сама должна вернуть значение ошибки (обычно `NULL` или `-1`). Она *не* должна вызывать одну из функций `PyErr_*` – одна уже была вызвана функцией *g*. Вызывающий код *f* затем также должен вернуть индикацию ошибки своему *своему* вызывающему, опять же *без* вызова `PyErr_*`, и так далее – наиболее подробная причина ошибки уже была сообщена функцией, которая первой её обнаружила. Как только ошибка достигает главного цикла интерпретатора Python, это прерывает выполняемый код Python и пытается найти обработчик исключения, указанный программистом на Python.2425(Бывают ситуации, когда модуль может дать более подробное сообщение об ошибке, вызвав другую функцию `PyErr_*`, и в таких случаях это допустимо. Однако, как правило, это не обязательно и может привести к потере информации о причине ошибки: большинство операций могут завершиться ошибкой по разным причинам.)2627Чтобы проигнорировать исключение, установленное неудачным вызовом функции, условие исключения должно быть явно очищено вызовом [`PyErr_Clear()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_Clear). Единственный случай, когда C-код должен вызывать `PyErr_Clear()`, – это если он не хочет передавать ошибку интерпретатору, а хочет полностью обработать её самостоятельно (возможно, попробовав что-то другое или сделав вид, что ничего не произошло).2829Каждый неудачный вызов `malloc()` должен быть преобразован в исключение – непосредственный вызывающий код `malloc()` (или `realloc()`) должен вызвать [`PyErr_NoMemory()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_NoMemory) и сам вернуть индикатор ошибки. Все функции, создающие объекты (например, [`PyLong_FromLong()`](https://python-all.ru/3.16/c-api/long.html#c.PyLong_FromLong)), уже делают это, поэтому данное замечание относится только к тем, кто вызывает `malloc()` напрямую.3031Также обратите внимание, что, за важным исключением [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple) и подобных, функции, возвращающие целочисленный статус, обычно возвращают положительное значение или ноль в случае успеха и `-1` в случае ошибки, как системные вызовы Unix.3233Наконец, следует позаботиться об очистке мусора (вызвав [`Py_XDECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_XDECREF) или [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) для уже созданных объектов) при возврате индикатора ошибки.3435Выбор, какое исключение возбуждать, полностью за вами. Существуют предварительно объявленные C-объекты, соответствующие всем встроенным исключениям Python, например [`PyExc_ZeroDivisionError`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyExc_ZeroDivisionError), которые можно использовать напрямую. Конечно, следует выбирать исключения разумно – не стоит использовать [`PyExc_TypeError`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyExc_TypeError) для обозначения того, что файл не удалось открыть (для этого вероятно следует использовать [`PyExc_OSError`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyExc_OSError)). Если что-то не так со списком аргументов, функция [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple) обычно возбуждает `PyExc_TypeError`. Если значение аргумента должно находиться в определённом диапазоне или удовлетворять другим условиям, подходит [`PyExc_ValueError`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyExc_ValueError).3637Также можно определить новое исключение, уникальное для вашего модуля. Самый простой способ сделать это – объявить статическую глобальную объектную переменную в начале файла:3839```c40static PyObject *SpamError = NULL;41```4243и инициализировать её вызовом [`PyErr_NewException()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_NewException) в функции [`Py_mod_exec`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_exec) модуля (`spam_module_exec()`):4445```c46SpamError = PyErr_NewException("spam.error", NULL, NULL);47```4849Поскольку `SpamError` – глобальная переменная, она будет перезаписана при каждой повторной инициализации модуля, когда вызывается функция [`Py_mod_exec`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_exec).5051Пока давайте избежим этой проблемы: мы заблокируем повторную инициализацию, возбудив [`ImportError`](https://python-all.ru/3.16/library/exceptions.html#ImportError):5253```c54static PyObject *SpamError = NULL;5556static int57spam_module_exec(PyObject *m)58{59    if (SpamError != NULL) {60        PyErr_SetString(PyExc_ImportError,61                        "cannot initialize spam module more than once");62        return -1;63    }64    SpamError = PyErr_NewException("spam.error", NULL, NULL);65    if (PyModule_AddObjectRef(m, "SpamError", SpamError) < 0) {66        return -1;67    }6869    return 0;70}7172static PyModuleDef_Slot spam_module_slots[] = {73    {Py_mod_exec, spam_module_exec},74    {0, NULL}75};7677static struct PyModuleDef spam_module = {78    .m_base = PyModuleDef_HEAD_INIT,79    .m_name = "spam",80    .m_size = 0,  // неотрицательное81    .m_slots = spam_module_slots,82};8384PyMODINIT_FUNC85PyInit_spam(void)86{87    return PyModuleDef_Init(&spam_module);88}89```9091Обратите внимание, что имя исключения в Python – `spam.error`. Функция [`PyErr_NewException()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_NewException) может создать класс с базовым классом [`Exception`](https://python-all.ru/3.16/library/exceptions.html#Exception) (если только не передан другой класс вместо `NULL`), описанный в [Встроенные исключения](https://python-all.ru/3.16/library/exceptions.html#bltin-exceptions).9293Также обратите внимание, что переменная `SpamError` сохраняет ссылку на вновь созданный класс исключения; это сделано намеренно! Поскольку исключение может быть удалено из модуля внешним кодом, необходима собственная ссылка на класс, чтобы гарантировать, что он не будет отброшен, что приведёт к превращению `SpamError` в висячий указатель. Если он станет висячим указателем, C-код, возбуждающий исключение, может вызвать аварийный дамп или другие непреднамеренные побочные эффекты.9495Пока отсутствует вызов [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) для удаления этой ссылки. Даже при завершении работы интерпретатора Python глобальная переменная `SpamError` не будет собрана сборщиком мусора. Она будет «утекать». Однако мы гарантировали, что это произойдёт не более одного раза на процесс.9697Использование [`PyMODINIT_FUNC`](https://python-all.ru/3.16/c-api/extension-modules.html#c.PyMODINIT_FUNC) в качестве возвращаемого типа функции обсуждается далее в этом примере.9899Исключение `spam.error` может быть возбуждено в вашем модуле расширения с помощью вызова [`PyErr_SetString()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_SetString), как показано ниже:100101```c102static PyObject *103spam_system(PyObject *self, PyObject *args)104{105    const char *command;106    int sts;107108    if (!PyArg_ParseTuple(args, "s", &command))109        return NULL;110    sts = system(command);111    if (sts < 0) {112        PyErr_SetString(SpamError, "System command failed");113        return NULL;114    }115    return PyLong_FromLong(sts);116}117```118119## Встраивание расширения120121Если вы хотите сделать ваш модуль постоянной частью интерпретатора Python, вам нужно изменить настройки конфигурации и пересобрать интерпретатор. В Unix поместите ваш файл (например, `spammodule.c`) в каталог `Modules/` распакованного дистрибутива исходных кодов, добавьте строку в файл `Modules/Setup.local`, описывающую ваш файл:122123```sh124spam spammodule.o125```126127и пересобрать интерпретатор, выполнив **make** в корневом каталоге. Также можно выполнить **make** в подкаталоге `Modules/`, но тогда сначала нужно пересобрать `Makefile` там, выполнив '**make** Makefile'. (Это необходимо каждый раз при изменении файла `Setup`.)128129Если ваш модуль требует дополнительных библиотек для компоновки, их также можно перечислить в строке конфигурационного файла, например:130131```sh132spam spammodule.o -lX11133```134135## Вызов функций Python из C136137Руководство было сосредоточено на том, чтобы сделать функции C вызываемыми из Python. Обратное также полезно: вызов функций Python из C. Это особенно актуально для библиотек, поддерживающих так называемые «колбэк»-функции. Если C-интерфейс использует колбэки, то соответствующий код на Python часто должен предоставить механизм колбэков для программиста на Python; реализация потребует вызова Python-колбэков из C-колбэка. Можно представить и другие применения.138139К счастью, интерпретатор Python легко вызывается рекурсивно, и существует стандартный интерфейс для вызова функции Python. (Если вас интересует, как вызвать парсер Python с определённой строкой на входе, обратитесь к разделу [Самый высокоуровневый слой](https://python-all.ru/3.16/c-api/veryhigh.html#veryhigh).)140141Вызвать функцию Python легко. Во-первых, программа на Python должна каким-то образом передать вам объект функции Python. Вы должны предоставить функцию (или какой-то другой интерфейс) для этого. Когда эта функция вызывается, сохраните указатель на объект функции Python (не забудьте [`Py_INCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_INCREF) её!) в глобальной переменной – или где сочтёте нужным. Например, следующая функция может быть частью определения модуля:142143```c144static PyObject *my_callback = NULL;145146static PyObject *147my_set_callback(PyObject *dummy, PyObject *args)148{149    PyObject *result = NULL;150    PyObject *temp;151152    if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {153        if (!PyCallable_Check(temp)) {154            PyErr_SetString(PyExc_TypeError, "parameter must be callable");155            return NULL;156        }157        Py_XINCREF(temp);         /* Добавить ссылку на новый колбэк */158        Py_XDECREF(my_callback);  /* Освободить предыдущий колбэк */159        my_callback = temp;       /* Запомнить новый колбэк */160        /* Шаблон для возврата None */161        Py_INCREF(Py_None);162        result = Py_None;163    }164    return result;165}166```167168Эта функция должна быть зарегистрирована в интерпретаторе с использованием флага [`METH_VARARGS`](https://python-all.ru/3.16/c-api/structures.html#c.METH_VARARGS) в [`PyMethodDef.ml_flags`](https://python-all.ru/3.16/c-api/structures.html#c.PyMethodDef.ml_flags). Функция [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple) и её аргументы описаны в разделе [Извлечение параметров в функциях расширения](https://python-all.ru/3.16/extending/extending.html#parsetuple).169170Макросы [`Py_XINCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_XINCREF) и [`Py_XDECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_XDECREF) увеличивают/уменьшают счётчик ссылок объекта и безопасны при наличии указателей `NULL` (но обратите внимание, что *temp* не будет `NULL` в этом контексте). Подробнее о них в разделе [Счётчики ссылок](https://python-all.ru/3.16/extending/extending.html#refcounts).171172Позже, когда придёт время вызвать функцию, вы вызываете C-функцию [`PyObject_CallObject()`](https://python-all.ru/3.16/c-api/call.html#c.PyObject_CallObject). Эта функция имеет два аргумента, оба указатели на произвольные объекты Python: функцию Python и список аргументов. Список аргументов всегда должен быть объектом кортежа, длина которого равна количеству аргументов. Для вызова функции Python без аргументов передайте `NULL` или пустой кортеж; для вызова с одним аргументом передайте кортеж из одного элемента. [`Py_BuildValue()`](https://python-all.ru/3.16/c-api/arg.html#c.Py_BuildValue) возвращает кортеж, если строка формата состоит из нуля или более кодов формата в скобках. Например:173174```c175int arg;176PyObject *arglist;177PyObject *result;178...179arg = 123;180...181/* Вызов колбэка */182arglist = Py_BuildValue("(i)", arg);183result = PyObject_CallObject(my_callback, arglist);184Py_DECREF(arglist);185```186187[`PyObject_CallObject()`](https://python-all.ru/3.16/c-api/call.html#c.PyObject_CallObject) возвращает указатель на объект Python: это возвращаемое значение функции Python. `PyObject_CallObject()` нейтрален по отношению к счётчику ссылок своих аргументов. В примере был создан новый кортеж для использования в качестве списка аргументов, который [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF)-ится сразу после вызова `PyObject_CallObject()`.188189Возвращаемое значение [`PyObject_CallObject()`](https://python-all.ru/3.16/c-api/call.html#c.PyObject_CallObject) является «новым»: это либо совершенно новый объект, либо существующий объект, чей счётчик ссылок был увеличен. Поэтому, если вы не хотите сохранять его в глобальной переменной, вы должны каким-то образом [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) результат, даже (особенно!) если вас не интересует его значение.190191Однако перед этим важно проверить, что возвращаемое значение не равно `NULL`. Если это так, функция Python завершилась возбуждением исключения. Если C-код, вызвавший [`PyObject_CallObject()`](https://python-all.ru/3.16/c-api/call.html#c.PyObject_CallObject), был вызван из Python, он должен вернуть индикатор ошибки своему вызывающему коду на Python, чтобы интерпретатор мог вывести трассировку стека, или вызывающий код на Python мог обработать исключение. Если это невозможно или нежелательно, исключение следует очистить вызовом [`PyErr_Clear()`](https://python-all.ru/3.16/c-api/exceptions.html#c.PyErr_Clear). Например:192193```c194if (result == NULL)195    return NULL; /* Передать ошибку обратно */196...use result...197Py_DECREF(result);198```199200В зависимости от желаемого интерфейса к Python-функции обратного вызова, вам также может потребоваться предоставить список аргументов для [`PyObject_CallObject()`](https://python-all.ru/3.16/c-api/call.html#c.PyObject_CallObject). В некоторых случаях список аргументов также предоставляется программой на Python через тот же интерфейс, который задал функцию обратного вызова. Затем его можно сохранить и использовать так же, как объект функции. В других случаях вам может потребоваться создать новый кортеж для передачи в качестве списка аргументов. Самый простой способ сделать это – вызвать [`Py_BuildValue()`](https://python-all.ru/3.16/c-api/arg.html#c.Py_BuildValue). Например, если вы хотите передать целочисленный код события, можно использовать следующий код:201202```c203PyObject *arglist;204...205arglist = Py_BuildValue("(l)", eventcode);206result = PyObject_CallObject(my_callback, arglist);207Py_DECREF(arglist);208if (result == NULL)209    return NULL; /* Передать ошибку обратно */210/* Здесь, возможно, используется результат */211Py_DECREF(result);212```213214Обратите внимание на размещение `Py_DECREF(arglist)` сразу после вызова, перед проверкой ошибки! Также обратите внимание, что строго говоря, этот код не полон: [`Py_BuildValue()`](https://python-all.ru/3.16/c-api/arg.html#c.Py_BuildValue) может исчерпать память, и это должно быть проверено.215216Вы также можете вызвать функцию с именованными аргументами, используя [`PyObject_Call()`](https://python-all.ru/3.16/c-api/call.html#c.PyObject_Call), который поддерживает аргументы и именованные аргументы. Как в примере выше, мы используем [`Py_BuildValue()`](https://python-all.ru/3.16/c-api/arg.html#c.Py_BuildValue) для создания словаря.217218```c219PyObject *dict;220...221dict = Py_BuildValue("{s:i}", "name", val);222result = PyObject_Call(my_callback, NULL, dict);223Py_DECREF(dict);224if (result == NULL)225    return NULL; /* Передать ошибку обратно */226/* Здесь, возможно, используется результат */227Py_DECREF(result);228```229230## Извлечение параметров в функциях расширения231232В [руководстве](https://python-all.ru/3.16/extending/first-extension-module.html#first-extension-module) используется функция «[`METH_O`](https://python-all.ru/3.16/c-api/structures.html#c.METH_O)», которая ограничена одним аргументом Python. Если нужно больше, можно использовать [`METH_VARARGS`](https://python-all.ru/3.16/c-api/structures.html#c.METH_VARARGS) вместо неё. С этим флагом C-функция будет получать [`tuple`](https://python-all.ru/3.16/library/stdtypes.html#tuple) аргументов вместо одного объекта.233234Для распаковки кортежа CPython предоставляет функцию [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple), объявленную следующим образом:235236```c237int PyArg_ParseTuple(PyObject *arg, const char *format, ...);238```239240Аргумент *arg* должен быть объектом кортежа, содержащим список аргументов, переданных из Python в C-функцию. Аргумент *format* должен быть строкой формата, чей синтаксис описан в разделе [Разбор аргументов и построение значений](https://python-all.ru/3.16/c-api/arg.html#arg-parsing) справочного руководства Python/C API. Остальные аргументы должны быть адресами переменных, чей тип определяется строкой формата.241242Например, чтобы получить один объект Python [`str`](https://python-all.ru/3.16/library/stdtypes.html#str) и превратить его в буфер C, можно использовать `"s"` в качестве строки формата:243244```c245const char *command;246if (!PyArg_ParseTuple(args, "s", &command)) {247    return NULL;248}249```250251Если в списке аргументов обнаружена ошибка, `PyArg_ParseTuple()` возвращает `NULL` (индикатор ошибки для функций, возвращающих указатели на объекты); ваша функция может вернуть `NULL`, полагаясь на исключение, установленное [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple).252253Обратите внимание, что хотя [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple) проверяет, что аргументы Python имеют требуемые типы, он не может проверить корректность адресов C-переменных, переданных в вызов: если там допущены ошибки, код, вероятно, аварийно завершится или, по крайней мере, перезапишет случайные биты в памяти. Поэтому следует быть осторожным!254255Обратите внимание, что любые ссылки на объекты Python, передаваемые вызывающему, являются *заимствованными* ссылками; уменьшать их счётчик ссылок не следует.256257Несколько примеров вызовов:258259```c260#include <Python.h>261```262263```c264int ok;265int i, j;266long k, l;267const char *s;268Py_ssize_t size;269270ok = PyArg_ParseTuple(args, ""); /* Без аргументов */271    /* Вызов Python: f() */272```273274```c275ok = PyArg_ParseTuple(args, "s", &s); /* Строка */276    /* Возможный вызов Python: f('whoops!') */277```278279```c280ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Два длинных целых и строка */281    /* Возможный вызов Python: f(1, 2, 'three') */282```283284```c285ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);286    /* Пара int и строка, размер которой также возвращается */287    /* Возможный вызов Python: f((1, 2), 'three') */288```289290```c291{292    const char *file;293    const char *mode = "r";294    int bufsize = 0;295    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);296    /* Строка и, опционально, ещё одна строка и целое число */297    /* Возможные вызовы Python:298       f('spam')299       f('spam', 'w')300       f('spam', 'wb', 100000) */301}302```303304```c305{306    int left, top, right, bottom, h, v;307    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",308             &left, &top, &right, &bottom, &h, &v);309    /* Прямоугольник и точка */310    /* Возможный вызов Python:311       f(((0, 0), (400, 300)), (10, 10)) */312}313```314315```c316{317    Py_complex c;318    ok = PyArg_ParseTuple(args, "D:myfunction", &c);319    /* комплексное число, также предоставляющее имя функции для сообщений об ошибках */320    /* Возможный вызов Python: myfunction(1+2j) */321}322```323324## Именованные параметры для функций-расширений325326Если требуется, чтобы функция также принимала [именованные аргументы](https://python-all.ru/3.16/glossary.html#term-keyword-argument), необходимо использовать флаг [`METH_KEYWORDS`](https://python-all.ru/3.16/c-api/structures.html#c.METH_KEYWORDS) в сочетании с [`METH_VARARGS`](https://python-all.ru/3.16/c-api/structures.html#c.METH_VARARGS). (`METH_KEYWORDS` также можно использовать с другими флагами; обратитесь к его документации за списком допустимых комбинаций.)327328В этом случае C-функция должна принимать третий параметр `PyObject *`, который будет словарём ключевых слов. Для разбора аргументов такой функции используется [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTupleAndKeywords).329330Функция [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) объявляется следующим образом:331332```c333int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,334                                const char *format, char * const *kwlist, ...);335```336337Параметры *arg* и *format* идентичны параметрам функции [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple). Параметр *kwdict* – это словарь ключевых слов, полученный в качестве третьего параметра от среды выполнения Python. Параметр *kwlist* – это список строк, завершающийся `NULL`, который идентифицирует параметры; имена сопоставляются с информацией о типах из *format* слева направо. В случае успеха [`PyArg_ParseTupleAndKeywords()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTupleAndKeywords) возвращает true, в противном случае возвращает false и возбуждает соответствующее исключение.338339> **Примечание**340>341> Вложенные кортежи не могут быть разобраны при использовании именованных аргументов! Именованные параметры, переданные, но отсутствующие в *kwlist*, приведут к возбуждению [`TypeError`](https://python-all.ru/3.16/library/exceptions.html#TypeError).342343Вот пример модуля, использующего ключевые слова, основанный на примере Джеффа Филбрика ([philbrick@hks.com](https://python-all.ru/3.16/extending/extending.html)):344345```c346#define PY_SSIZE_T_CLEAN347#include <Python.h>348349static PyObject *350keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)351{352    int voltage;353    const char *state = "a stiff";354    const char *action = "voom";355    const char *type = "Norwegian Blue";356357    static char *kwlist[] = {"voltage", "state", "action", "type", NULL};358359    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,360                                     &voltage, &state, &action, &type))361        return NULL;362363    printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",364           action, voltage);365    printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);366367    Py_RETURN_NONE;368}369370static PyMethodDef keywdarg_methods[] = {371    /* Приведение функции необходимо, поскольку значения PyCFunction372     * принимают только два параметра PyObject*, а keywdarg_parrot() принимает373     * три.374     */375    {"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,376     "Print a lovely skit to standard output."},377    {NULL, NULL, 0, NULL}   /* сторожевое значение */378};379```380381## Построение произвольных значений382383Эта функция является аналогом [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple). Она объявляется следующим образом:384385```c386PyObject *Py_BuildValue(const char *format, ...);387```388389Она распознаёт набор форматных единиц, аналогичный распознаваемым [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple), но аргументы (которые являются входными для функции, а не выходными) не должны быть указателями, только значениями. Она возвращает новый объект Python, подходящий для возврата из C-функции, вызванной из Python.390391Одно отличие от [`PyArg_ParseTuple()`](https://python-all.ru/3.16/c-api/arg.html#c.PyArg_ParseTuple): последняя требует, чтобы её первый аргумент был кортежем (поскольку списки аргументов Python внутренне всегда представлены как кортежи), тогда как [`Py_BuildValue()`](https://python-all.ru/3.16/c-api/arg.html#c.Py_BuildValue) не всегда строит кортеж. Она строит кортеж, только если строка формата содержит две или более форматных единиц. Если строка формата пуста, она возвращает `None`; если она содержит ровно одну форматную единицу, она возвращает объект, описываемый этой единицей. Чтобы принудительно вернуть кортеж размера 0 или 1, строку формата следует заключить в скобки.392393Примеры (слева вызов, справа результирующее значение Python):394395```text396Py_BuildValue("")                        None397Py_BuildValue("i", 123)                  123398Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)399Py_BuildValue("s", "hello")              'hello'400Py_BuildValue("y", "hello")              b'hello'401Py_BuildValue("ss", "hello", "world")    ('hello', 'world')402Py_BuildValue("s#", "hello", 4)          'hell'403Py_BuildValue("y#", "hello", 4)          b'hell'404Py_BuildValue("()")                      ()405Py_BuildValue("(i)", 123)                (123,)406Py_BuildValue("(ii)", 123, 456)          (123, 456)407Py_BuildValue("(i,i)", 123, 456)         (123, 456)408Py_BuildValue("[i,i]", 123, 456)         [123, 456]409Py_BuildValue("{s:i,s:i}",410              "abc", 123, "def", 456)    {'abc': 123, 'def': 456}411Py_BuildValue("((ii)(ii)) (ii)",412              1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))413```414415## Счетчики ссылок416417В таких языках, как C или C++, программист отвечает за динамическое выделение и освобождение памяти в куче. В C это делается с помощью функций `malloc()` и `free()`. В C++ операторы `new` и `delete` используются с тем же смыслом; дальнейшее обсуждение ограничивается случаем C.418419Каждый блок памяти, выделенный через `malloc()`, в конечном итоге должен быть возвращён в пул доступной памяти ровно одним вызовом `free()`. Важно вызвать `free()` в нужный момент. Если адрес блока забыт, но `free()` для него не вызвана, занимаемая им память не может быть повторно использована до завершения программы. Это называется *утечкой памяти*. С другой стороны, если программа вызывает `free()` для блока, а затем продолжает его использовать, это создаёт конфликт с повторным использованием блока через другой вызов `malloc()`. Это называется *использованием освобождённой памяти*. Это имеет те же негативные последствия, что и обращение к неинициализированным данным – дампы памяти, неверные результаты, таинственные аварийные завершения.420421Распространённые причины утечек памяти – нестандартные пути выполнения кода. Например, функция может выделить блок памяти, выполнить некоторые вычисления, а затем снова освободить блок. Теперь изменение требований к функции может добавить в вычисление проверку, которая обнаруживает ошибочную ситуацию и может привести к преждевременному возврату из функции. Легко забыть освободить выделенный блок памяти при таком преждевременном выходе, особенно если он был добавлен в код позднее. Такие утечки, будучи однажды внесёнными, часто остаются незамеченными долгое время: ошибочный выход происходит лишь в небольшой доле всех вызовов, а на большинстве современных машин достаточно виртуальной памяти, поэтому утечка становится заметна только в долго работающем процессе, который часто использует функцию с утечкой. Следовательно, важно предотвращать утечки, используя соглашение о кодировании или стратегию, которая минимизирует такого рода ошибки.422423Поскольку Python активно использует `malloc()` и `free()`, ему нужна стратегия для предотвращения как утечек памяти, так и использования освобождённой памяти. Выбранный метод называется *подсчётом ссылок*. Принцип прост: каждый объект содержит счётчик, который увеличивается, когда ссылка на объект сохраняется где-либо, и уменьшается, когда ссылка на него удаляется. Когда счётчик достигает нуля, последняя ссылка на объект удалена, и объект освобождается.424425Альтернативная стратегия называется *автоматической сборкой мусора*. (Иногда подсчёт ссылок также называют стратегией сборки мусора, поэтому для различия используется термин «автоматическая».) Большое преимущество автоматической сборки мусора в том, что пользователю не нужно явно вызывать `free()`. (Другим заявляемым преимуществом является повышение скорости или эффективности использования памяти – однако это не является строгим фактом.) Недостаток в том, что для C не существует действительно переносимого автоматического сборщика мусора, в то время как подсчёт ссылок может быть реализован переносимо (при условии, что доступны функции `malloc()` и `free()` – что гарантируется стандартом C). Возможно, когда-нибудь появится достаточно переносимый автоматический сборщик мусора для C. А до тех пор придётся мириться с подсчётом ссылок.426427Хотя Python использует традиционную реализацию подсчёта ссылок, он также предлагает детектор циклов, который обнаруживает циклические ссылки. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок; они являются слабым местом сборки мусора, реализованной только с помощью подсчёта ссылок. Циклические ссылки состоят из объектов, которые содержат (возможно, косвенные) ссылки на самих себя, так что каждый объект в цикле имеет ненулевой счётчик ссылок. Типичные реализации подсчёта ссылок не могут освободить память, принадлежащую объектам в циклической ссылке или на которые есть ссылки из объектов в цикле, даже если на сам цикл больше нет ссылок.428429Детектор циклов способен обнаруживать мусорные циклы и может их освобождать. Модуль [`gc`](https://python-all.ru/3.16/library/gc.html#module-gc) предоставляет способ запуска детектора (функция [`collect()`](https://python-all.ru/3.16/library/gc.html#gc.collect)), а также интерфейсы конфигурации и возможность отключения детектора во время выполнения.430431### Подсчёт ссылок в Python432433Существуют два макроса, `Py_INCREF(x)` и `Py_DECREF(x)`, которые управляют увеличением и уменьшением счётчика ссылок. [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) также освобождает объект, когда счётчик достигает нуля. Для гибкости он не вызывает `free()` напрямую – вместо этого он выполняет вызов через указатель на функцию в *объекте типа* объекта. Для этой цели (и других) каждый объект также содержит указатель на свой объект типа.434435Остаётся главный вопрос: когда использовать `Py_INCREF(x)` и `Py_DECREF(x)`? Сначала введём несколько терминов. Никто не «владеет» объектом; однако можно *владеть ссылкой* на объект. Счётчик ссылок объекта теперь определяется как количество принадлежащих (owned) ссылок на него. Владелец ссылки отвечает за вызов [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF), когда ссылка больше не нужна. Владение ссылкой может быть передано. Существует три способа распорядиться принадлежащей ссылкой: передать её, сохранить или вызвать `Py_DECREF()`. Забыть распорядиться принадлежащей ссылкой – значит создать утечку памяти.436437Также возможно *заимствовать* [\[1\]](https://python-all.ru/3.16/extending/extending.html#borrow) ссылку на объект. Заимствующий ссылку не должен вызывать [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF). Заимствующий не должен удерживать объект дольше, чем владелец, у которого она была заимствована. Использование заимствованной ссылки после того, как владелец распорядился ею, связано с риском использования освобождённой памяти и должно полностью избегаться [\[2\]](https://python-all.ru/3.16/extending/extending.html#dont-check-refcount).438439Преимущество заимствования ссылки перед владением в том, что не нужно заботиться о распоряжении ссылкой на всех возможных путях выполнения кода – иными словами, с заимствованной ссылкой нет риска утечки при преждевременном выходе. Недостаток заимствования по сравнению с владением в том, что существуют некоторые тонкие ситуации, когда в, казалось бы, корректном коде заимствованная ссылка может быть использована после того, как владелец, у которого она была заимствована, фактически распорядился ею.440441Заимствованная ссылка может быть превращена в собственную путём вызова [`Py_INCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_INCREF). Это не влияет на статус владельца, у которого была заимствована ссылка – создаётся новая собственная ссылка, и на нового владельца возлагаются все обязанности владельца (новый владелец должен правильно распорядиться ссылкой, равно как и предыдущий владелец).442443### Правила владения444445Всякий раз, когда ссылка на объект передаётся в функцию или из неё, является частью спецификации интерфейса функции, передаётся ли владение вместе со ссылкой или нет.446447Большинство функций, возвращающих ссылку на объект, передают вместе с ней и владение. В частности, все функции, чья задача – создать новый объект, такие как [`PyLong_FromLong()`](https://python-all.ru/3.16/c-api/long.html#c.PyLong_FromLong) и [`Py_BuildValue()`](https://python-all.ru/3.16/c-api/arg.html#c.Py_BuildValue), передают владение получателю. Даже если объект на самом деле не новый, всё равно получается владение новой ссылкой на этот объект. Например, `PyLong_FromLong()` поддерживает кеш популярных значений и может вернуть ссылку на кешированный элемент.448449Многие функции, извлекающие объекты из других объектов, также передают владение вместе со ссылкой, например [`PyObject_GetAttrString()`](https://python-all.ru/3.16/c-api/object.html#c.PyObject_GetAttrString). Однако здесь картина менее ясна, поскольку несколько распространённых подпрограмм являются исключениями: [`PyTuple_GetItem()`](https://python-all.ru/3.16/c-api/tuple.html#c.PyTuple_GetItem), [`PyList_GetItem()`](https://python-all.ru/3.16/c-api/list.html#c.PyList_GetItem), [`PyDict_GetItem()`](https://python-all.ru/3.16/c-api/dict.html#c.PyDict_GetItem) и [`PyDict_GetItemString()`](https://python-all.ru/3.16/c-api/dict.html#c.PyDict_GetItemString) – все они возвращают ссылки, которые заимствуются из кортежа, списка или словаря.450451Функция [`PyImport_AddModule()`](https://python-all.ru/3.16/c-api/import.html#c.PyImport_AddModule) также возвращает заимствованную ссылку, даже если она на самом деле создаёт возвращаемый объект: это возможно, потому что собственная ссылка на объект хранится в `sys.modules`.452453Когда вы передаёте ссылку на объект в другую функцию, в общем случае функция заимствует у вас эту ссылку – если ей нужно сохранить её, она использует [`Py_INCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_INCREF), чтобы стать независимым владельцем. Из этого правила есть ровно два важных исключения: [`PyTuple_SetItem()`](https://python-all.ru/3.16/c-api/tuple.html#c.PyTuple_SetItem) и [`PyList_SetItem()`](https://python-all.ru/3.16/c-api/list.html#c.PyList_SetItem). Эти функции принимают владение переданным им объектом – даже в случае неудачи! (Обратите внимание, что [`PyDict_SetItem()`](https://python-all.ru/3.16/c-api/dict.html#c.PyDict_SetItem) и подобные ему функции не принимают владение – они «нормальные».)454455Когда C-функция вызывается из Python, она заимствует ссылки на свои аргументы у вызывающей стороны. Вызывающая сторона владеет ссылкой на объект, поэтому время жизни заимствованной ссылки гарантировано до возврата из функции. Только если такую заимствованную ссылку необходимо сохранить или передать дальше, она должна быть превращена в собственную ссылку с помощью вызова [`Py_INCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_INCREF).456457Ссылка на объект, возвращаемая из C-функции, вызванной из Python, должна быть собственной – владение передаётся от функции её вызывающей стороне.458459### Тонкий лёд460461Есть несколько ситуаций, когда безобидное, на первый взгляд, использование заимствованной ссылки может привести к проблемам. Все они связаны с неявными вызовами интерпретатора, которые могут заставить владельца ссылки освободить её.462463Первый и самый важный случай, о котором стоит знать, – это использование [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) на постороннем объекте, пока заимствована ссылка на элемент списка. Например:464465```c466void467bug(PyObject *list)468{469    PyObject *item = PyList_GetItem(list, 0);470471    PyList_SetItem(list, 1, PyLong_FromLong(0L));472    PyObject_Print(item, stdout, 0); /* ОШИБКА! */473}474```475476Эта функция сначала заимствует ссылку на `list[0]`, затем заменяет `list[1]` значением `0` и наконец печатает заимствованную ссылку. Выглядит безобидно, правда? Но это не так!477478Давайте проследим поток управления внутри [`PyList_SetItem()`](https://python-all.ru/3.16/c-api/list.html#c.PyList_SetItem). Список владеет ссылками на все свои элементы, поэтому при замене элемента 1 ему приходится освобождать исходный элемент 1. Теперь предположим, что исходный элемент 1 был экземпляром пользовательского класса, и далее предположим, что в этом классе определён метод `__del__()`. Если у этого экземпляра класса счётчик ссылок равен 1, то его освобождение приведёт к вызову метода `__del__()`. Внутренне `PyList_SetItem()` вызывает [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) для заменённого элемента, что активирует соответствующую функцию [`tp_dealloc`](https://python-all.ru/3.16/c-api/typeobj.html#c.PyTypeObject.tp_dealloc) заменённого элемента. Во время освобождения `tp_dealloc` вызывает [`tp_finalize`](https://python-all.ru/3.16/c-api/typeobj.html#c.PyTypeObject.tp_finalize), которая отображается на метод `__del__()` для экземпляров классов (см. [**PEP 442**](https://python-all.ru/3.16/extending/extending.html)). Вся эта последовательность выполняется синхронно внутри вызова `PyList_SetItem()`.479480Поскольку метод `__del__()` написан на Python, он может выполнять произвольный код Python. Может ли он каким-то образом аннулировать ссылку на `item` в `bug()`? Ещё как! Если предположить, что список, переданный в `bug()`, доступен методу `__del__()`, то он может выполнить инструкцию вроде `del list[0]`, и если это была последняя ссылка на этот объект, то он освободит связанную с ним память, тем самым аннулировав `item`.481482Решение, если знать источник проблемы, простое: временно увеличить счётчик ссылок. Правильная версия функции выглядит так:483484```c485void486no_bug(PyObject *list)487{488    PyObject *item = PyList_GetItem(list, 0);489490    Py_INCREF(item);491    PyList_SetItem(list, 1, PyLong_FromLong(0L));492    PyObject_Print(item, stdout, 0);493    Py_DECREF(item);494}495```496497Это реальная история. В старой версии Python были варианты этой ошибки, и кому-то пришлось потратить много времени в отладчике C, чтобы выяснить, почему его методы `__del__()` не работали…498499Второй случай проблем с заимствованной ссылкой – это вариант, связанный с потоками. Обычно несколько потоков в интерпретаторе Python не мешают друг другу, потому что существует [глобальная блокировка](https://python-all.ru/3.16/glossary.html#term-global-interpreter-lock), защищающая всё пространство объектов Python. Однако эту блокировку можно временно отпустить с помощью макроса [`Py_BEGIN_ALLOW_THREADS`](https://python-all.ru/3.16/c-api/threads.html#c.Py_BEGIN_ALLOW_THREADS), а затем захватить снова с помощью [`Py_END_ALLOW_THREADS`](https://python-all.ru/3.16/c-api/threads.html#c.Py_END_ALLOW_THREADS). Это часто используется вокруг блокирующих операций ввода-вывода, чтобы дать другим потокам возможность использовать процессор в ожидании завершения ввода-вывода. Очевидно, что следующая функция имеет ту же проблему, что и предыдущая:500501```c502void503bug(PyObject *list)504{505    PyObject *item = PyList_GetItem(list, 0);506    Py_BEGIN_ALLOW_THREADS507    ...some blocking I/O call...508    Py_END_ALLOW_THREADS509    PyObject_Print(item, stdout, 0); /* ОШИБКА! */510}511```512513### Нулевые указатели514515В общем случае функции, принимающие ссылки на объекты в качестве аргументов, не ожидают, что вы передадите им указатели `NULL`, и приведут к аварийному завершению (или более поздним сбоям), если вы это сделаете. Функции, возвращающие ссылки на объекты, обычно возвращают `NULL` только для указания на то, что произошло исключение. Причина, по которой не проверяются аргументы на `NULL`, в том, что функции часто передают полученные объекты другим функциям – если бы каждая функция проверяла `NULL`, было бы много избыточных проверок и код работал бы медленнее.516517Лучше проверять на `NULL` только «у источника»: когда получен указатель, который может быть `NULL`, например, от `malloc()` или от функции, которая может возбуждать исключение.518519Макросы [`Py_INCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_INCREF) и [`Py_DECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_DECREF) не проверяют указатели на `NULL` – однако их варианты [`Py_XINCREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_XINCREF) и [`Py_XDECREF()`](https://python-all.ru/3.16/c-api/refcounting.html#c.Py_XDECREF) делают это.520521Макросы для проверки конкретного типа объекта (`Pytype_Check()`) не проверяют указатели на `NULL` – опять же, есть много кода, который вызывает несколько таких макросов подряд для проверки объекта на соответствие различным ожидаемым типам, и это порождало бы избыточные проверки. Не существует вариантов с проверкой `NULL`.522523Механизм вызова C-функций гарантирует, что список аргументов, передаваемый C- функциям (`args` в примерах), никогда не равен `NULL` – на самом деле он гарантирует, что это всегда кортеж [\[3\]](https://python-all.ru/3.16/extending/extending.html#old-calling-convention).524525Серьёзной ошибкой является допустить «утечку» указателя `NULL` к пользователю Python.526527## Написание расширений на C++528529Модули расширения можно писать на C++. Применяются некоторые ограничения. Если главная программа (интерпретатор Python) компилируется и компонуется C- компилятором, нельзя использовать глобальные или статические объекты с конструкторами. Это не проблема, если главная программа компонуется C++-компилятором. Функции, которые будут вызываться интерпретатором Python (в частности, функции инициализации модулей), должны быть объявлены с помощью `extern "C"`. Нет необходимости заключать заголовочные файлы Python в `extern "C" {...}` – они уже используют эту форму, если определён символ `__cplusplus` (все современные C++-компиляторы определяют этот символ).530531## Предоставление C API для модуля расширения532533Многие модули расширения просто предоставляют новые функции и типы для использования из Python, но иногда код в модуле расширения может быть полезен для других модулей расширения. Например, модуль расширения может реализовать тип «коллекция», который работает как список без порядка. Подобно тому, как стандартный тип списка Python имеет C API, позволяющий модулям расширения создавать списки и управлять ими, этот новый тип коллекции должен иметь набор C-функций для прямого управления из других модулей расширения.534535На первый взгляд задача кажется простой: достаточно написать функции (не объявляя их `static`, разумеется), предоставить соответствующий заголовочный файл и документировать C API. И действительно, это бы сработало, если бы все модули расширения всегда статически компоновались с интерпретатором Python. Однако, когда модули используются как разделяемые библиотеки, символы, определённые в одном модуле, могут быть не видны другому модулю. Детали видимости зависят от операционной системы; одни системы используют единое глобальное пространство имён для интерпретатора Python и всех модулей расширения (например, Windows), а другие требуют явного списка импортируемых символов на этапе компоновки модуля (например, AIX) или предлагают выбор разных стратегий (большинство Unix-систем). И даже если символы глобально видны, модуль, функции которого нужно вызвать, может быть ещё не загружен!536537Таким образом, переносимость требует не делать никаких предположений о видимости символов. Это означает, что все символы в модулях расширения должны быть объявлены `static`, за исключением функции инициализации модуля, чтобы избежать конфликтов имён с другими модулями расширения. А также означает, что символы, которые *должны* быть доступны из других модулей расширения, должны экспортироваться иным способом.538539Python предоставляет специальный механизм для передачи C-информации (указателей) из одного модуля расширения в другой: Capsules. Capsule – это тип данных Python, который хранит указатель (void\*). Capsules можно создавать и получать доступ к ним только через их C API, но их можно передавать, как любой другой объект Python. В частности, их можно присвоить имени в пространстве имён модуля расширения. Другие модули расширения могут затем импортировать этот модуль, получить значение этого имени, а затем извлечь указатель из Capsule.540541Существует много способов использования Capsules для экспорта C API модуля расширения. Каждая функция может получить свой собственный Capsule, или все указатели C API могут храниться в массиве, адрес которого опубликован в Capsule. А различные задачи хранения и извлечения указателей могут быть распределены разными способами между модулем, предоставляющим код, и модулями-клиентами.542543Какой бы метод вы ни выбрали, важно правильно называть свои Capsules. Функция [`PyCapsule_New()`](https://python-all.ru/3.16/c-api/capsule.html#c.PyCapsule_New) принимает параметр name (const char\*); вы можете передать `NULL` имя, но мы настоятельно рекомендуем указывать имя. Правильно названные Capsules обеспечивают определённый уровень типобезопасности во время выполнения; нет практического способа отличить один безымянный Capsule от другого.544545В частности, Capsules, используемые для предоставления C API, должны получать имена согласно следующему соглашению:546547```c548modulename.attributename549```550551Удобная функция [`PyCapsule_Import()`](https://python-all.ru/3.16/c-api/capsule.html#c.PyCapsule_Import) упрощает загрузку C API, предоставленного через Capsule, но только если имя Capsule соответствует этому соглашению. Такое поведение даёт пользователям C API высокую степень уверенности в том, что загруженный ими Capsule содержит правильный C API.552553Следующий пример демонстрирует подход, при котором основная нагрузка ложится на разработчика экспортирующего модуля, что подходит для часто используемых библиотечных модулей. Он хранит все указатели C API (в примере только один!) в массиве указателей void, который становится значением Capsule. Заголовочный файл, соответствующий модулю, предоставляет макрос, который заботится об импорте модуля и извлечении его указателей C API; модулям-клиентам нужно только вызвать этот макрос перед доступом к C API.554555Экспортирующий модуль является модификацией модуля `spam` из [учебника](https://python-all.ru/3.16/extending/first-extension-module.html#first-extension-module). Функция `spam.system()` не вызывает напрямую функцию библиотеки C `system()`, а вызывает функцию `PySpam_System()`, которая в реальности, конечно, делала бы что-то более сложное (например, добавляла «spam» к каждой команде). Эта функция `PySpam_System()` также экспортируется в другие модули расширения.556557Функция `PySpam_System()` – это обычная функция на C, объявленная `static`, как и всё остальное:558559```c560static int561PySpam_System(const char *command)562{563    return system(command);564}565```566567Функция `spam_system()` тривиально модифицирована:568569```c570static PyObject *571spam_system(PyObject *self, PyObject *args)572{573    const char *command;574    int sts;575576    if (!PyArg_ParseTuple(args, "s", &command))577        return NULL;578    sts = PySpam_System(command);579    return PyLong_FromLong(sts);580}581```582583В начале модуля, сразу после строки584585```c586#include <Python.h>587```588589необходимо добавить ещё две строки:590591```c592#define SPAM_MODULE593#include "spammodule.h"594```595596`#define` используется, чтобы сообщить заголовочному файлу, что он включается в экспортирующий модуль, а не в клиентский модуль. Наконец, функция [`mod_exec`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_exec) модуля должна позаботиться об инициализации массива указателей C API:597598```c599static int600spam_module_exec(PyObject *m)601{602    static void *PySpam_API[PySpam_API_pointers];603    PyObject *c_api_object;604605    /* Инициализация массива указателей C API */606    PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;607608    /* Создание капсулы, содержащей адрес массива указателей API */609    c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);610611    if (PyModule_Add(m, "_C_API", c_api_object) < 0) {612        return -1;613    }614615    return 0;616}617```618619Обратите внимание, что `PySpam_API` объявлен `static`; иначе массив указателей исчезнет после завершения `PyInit_spam()`!620621Основная работа выполняется в заголовочном файле `spammodule.h`, который выглядит следующим образом:622623```c624#ifndef Py_SPAMMODULE_H625#define Py_SPAMMODULE_H626#ifdef __cplusplus627extern "C" {628#endif629630/* Заголовочный файл для spammodule */631632/* Функции C API */633#define PySpam_System_NUM 0634#define PySpam_System_RETURN int635#define PySpam_System_PROTO (const char *command)636637/* Общее количество указателей C API */638#define PySpam_API_pointers 1639640#ifdef SPAM_MODULE641/* Этот раздел используется при компиляции spammodule.c */642643static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;644645#else646/* Этот раздел используется в модулях, которые используют API spammodule */647648static void **PySpam_API;649650#define PySpam_System \651 (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])652653/* Возвращает -1 при ошибке, 0 при успехе.654 * PyCapsule_Import установит исключение, если произошла ошибка.655 */656static int657import_spam(void)658{659    PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);660    return (PySpam_API != NULL) ? 0 : -1;661}662663#endif664665#ifdef __cplusplus666}667#endif668669#endif /* !defined(Py_SPAMMODULE_H) */670```671672Всё, что нужно сделать клиентскому модулю для получения доступа к функции `PySpam_System()`, – это вызвать функцию (или, точнее, макрос) `import_spam()` в своей функции [`mod_exec`](https://python-all.ru/3.16/c-api/module.html#c.Py_mod_exec):673674```c675static int676client_module_exec(PyObject *m)677{678    if (import_spam() < 0) {679        return -1;680    }681    /* дополнительная инициализация может быть выполнена здесь */682    return 0;683}684```685686Главный недостаток этого подхода в том, что файл `spammodule.h` довольно сложен. Однако базовая структура одинакова для каждой экспортируемой функции, поэтому её достаточно изучить один раз.687688Наконец, стоит упомянуть, что Capsules предоставляют дополнительную функциональность, которая особенно полезна для выделения и освобождения памяти указателя, хранящегося в Capsule. Подробности описаны в справочном руководстве по Python/C API в разделе [Capsules](https://python-all.ru/3.16/c-api/capsule.html#capsules) и в реализации Capsules (файлы `Include/pycapsule.h` и `Objects/pycapsule.c` в дистрибутиве исходного кода Python).689690Примечания691692\[[1](https://python-all.ru/3.16/extending/extending.html#id1)\]693694Метафора «заимствования» ссылки не совсем верна: владелец всё ещё хранит копию ссылки.695696\[[2](https://python-all.ru/3.16/extending/extending.html#id2)\]697698Проверка того, что счётчик ссылок не меньше 1, **не работает** – сам счётчик ссылок может находиться в освобождённой памяти и, таким образом, быть повторно использован для другого объекта!699700\[[3](https://python-all.ru/3.16/extending/extending.html#id3)\]701702Эти гарантии не действуют при использовании «старого» стиля соглашения о вызовах – он всё ещё встречается во многих существующих программах.703