1. Расширение Python с помощью C или C++

Добавить новые встроенные модули в Python довольно просто, если вы знаете, как программировать на C. Такие extension modules могут делать две вещи, которые нельзя сделать непосредственно в Python: они могут реализовать новые встроенные типы объектов и могут вызывать функции библиотеки C и системные вызовы.

Для поддержки расширений Python API (Application Programmers Interface) определяет набор функций, макросов и переменных, которые обеспечивают доступ к большинству аспектов системы времени выполнения Python. Python API включается в исходный файл на языке C путем включения заголовка "Python.h".

Компиляция модуля расширения зависит от его назначения, а также от настроек вашей системы; подробности описаны в последующих главах.

Примечание

Интерфейс расширений C специфичен для CPython, и модули расширений не работают в других реализациях Python. Во многих случаях можно обойтись без написания расширений C и сохранить переносимость на другие реализации. Например, если ваш случай использования связан с вызовом функций библиотеки C или системных вызовов, вам следует рассмотреть возможность использования модуля ctypes или библиотеки cffi, а не писать собственный код на C. Эти модули позволяют писать код Python для взаимодействия с кодом C и более переносимы между реализациями Python, чем написание и компиляция модуля расширения C.

1.1. Простой пример

Давайте создадим модуль расширения под названием spam (любимая еда фанатов Монти Пайтона…) и предположим, что мы хотим создать интерфейс Python для функции библиотеки C system(). [1]. Эта функция принимает в качестве аргумента строку символов с нулевым окончанием и возвращает целое число. Мы хотим, чтобы эта функция вызывалась из Python следующим образом:

>>> import spam
>>> status = spam.system("ls -l")

Начните с создания файла spammodule.c. (Исторически сложилось так, что если модуль называется spam, то Си-файл, содержащий его реализацию, называется spammodule.c; если имя модуля очень длинное, например spammify, то имя модуля может быть просто spammify.c).

Первые две строки нашего файла могут быть такими:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

который использует Python API (при желании вы можете добавить комментарий, описывающий назначение модуля, и уведомление об авторских правах).

Примечание

Поскольку в Python могут быть определены некоторые определения препроцессора, которые влияют на стандартные заголовки в некоторых системах, вы должны включить Python.h до включения любых стандартных заголовков.

#define PY_SSIZE_T_CLEAN использовался для указания на то, что в некоторых API вместо int следует использовать Py_ssize_t. Начиная с Python 3.13 в нем нет необходимости, но мы оставляем его здесь для обратной совместимости. Описание этого макроса см. в Строки и буферы.

Все видимые пользователю символы, определяемые Python.h, имеют префикс Py или PY, за исключением тех, которые определены в стандартных заголовочных файлах. Для удобства и поскольку они широко используются интерпретатором Python, "Python.h" включает несколько стандартных заголовочных файлов: <stdio.h>, <string.h>, <errno.h> и <stdlib.h>. Если последний заголовочный файл не существует в вашей системе, он объявляет функции malloc(), free() и realloc() напрямую.

Следующее, что мы добавим в наш файл модуля, - это функция C, которая будет вызываться при оценке выражения Python spam.system(string) (мы вскоре увидим, как она будет вызываться):

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    return PyLong_FromLong(sts);
}

Существует прямой перевод из списка аргументов в Python (например, единственного выражения "ls -l") в аргументы, передаваемые в функцию C. Функция Си всегда имеет два аргумента, условно названных self и args.

Аргумент self указывает на объект модуля для функций уровня модуля; для метода он будет указывать на экземпляр объекта.

Аргумент args будет указателем на объект Python tuple, содержащий аргументы. Каждый элемент кортежа соответствует аргументу в списке аргументов вызова. Аргументы являются объектами Python - чтобы что-то сделать с ними в нашей C-функции, мы должны преобразовать их в C-значения. Функция PyArg_ParseTuple() в Python API проверяет типы аргументов и преобразует их в значения C. Она использует шаблонную строку для определения требуемых типов аргументов, а также типов переменных C, в которых будут храниться преобразованные значения. Подробнее об этом позже.

PyArg_ParseTuple() возвращает true (ненулевое значение), если все аргументы имеют правильный тип и их компоненты были сохранены в переменных, адреса которых переданы. Возвращает false (ноль), если был передан некорректный список аргументов. В последнем случае он также вызывает соответствующее исключение, так что вызывающая функция может сразу же вернуть NULL (как мы видели в примере).

1.2. Интермеццо: Ошибки и исключения

Важным соглашением в интерпретаторе Python является следующее: когда функция не работает, она должна установить условие исключения и вернуть значение ошибки (обычно -1 или указатель NULL). Информация об исключениях хранится в трех членах состояния потока интерпретатора. Они равны NULL, если исключение отсутствует. В противном случае они являются эквивалентами членов кортежа Python, возвращаемых sys.exc_info(). Это тип исключения, экземпляр исключения и объект traceback. Их важно знать, чтобы понять, как передаются ошибки.

API Python определяет ряд функций для установки различных типов исключений.

Наиболее распространенным является PyErr_SetString(). Его аргументами являются объект исключения и строка C. Объект исключения обычно представляет собой предопределенный объект, например PyExc_ZeroDivisionError. Строка C указывает на причину ошибки, преобразуется в строковый объект Python и сохраняется как «ассоциированное значение» исключения.

Еще одна полезная функция - PyErr_SetFromErrno(), которая принимает только аргумент исключения и строит связанное с ним значение путем проверки глобальной переменной errno. Наиболее общей функцией является PyErr_SetObject(), которая принимает два аргумента-объекта: исключение и связанное с ним значение. Вам не нужно Py_INCREF() передавать объекты ни в одну из этих функций.

Вы можете неразрушающим образом проверить, было ли установлено исключение, с помощью PyErr_Occurred(). Она возвращает текущий объект исключения или NULL, если исключение не произошло. Обычно вам не нужно вызывать PyErr_Occurred(), чтобы узнать, произошла ли ошибка при вызове функции, поскольку вы должны быть в состоянии определить это по возвращаемому значению.

Когда функция f, вызывающая другую функцию g, обнаруживает, что последняя не работает, f должна сама вернуть значение ошибки (обычно NULL или -1). Она не должна вызывать одну из функций ``PyErr_*`` - одна из них уже была вызвана *g. Вызывающая функция f также должна вернуть индикацию ошибки своему вызывающему, опять же без вызова PyErr_*, и так далее - наиболее подробная причина ошибки уже была сообщена функцией, которая первой ее обнаружила. Как только ошибка достигает главного цикла интерпретатора Python, он прерывает текущий выполняющийся код Python и пытается найти обработчик исключений, указанный программистом Python.

(Бывают ситуации, когда модуль может выдать более подробное сообщение об ошибке, вызвав другую функцию PyErr_*, и в таких случаях можно поступить именно так. Однако, как правило, в этом нет необходимости, и это может привести к потере информации о причине ошибки: большинство операций могут не выполняться по разным причинам).

Чтобы проигнорировать исключение, установленное неудачным вызовом функции, условие исключения должно быть очищено явным образом вызовом PyErr_Clear(). Единственный случай, когда код на языке Си должен вызывать PyErr_Clear(), - это если он не хочет передавать ошибку интерпретатору, а хочет полностью справиться с ней самостоятельно (возможно, попробовав что-то другое или сделав вид, что ничего не произошло).

Каждый неудачный вызов malloc() должен быть превращен в исключение - непосредственный пользователь malloc() (или realloc()) должен вызвать PyErr_NoMemory() и сам вернуть индикатор неудачи. Все функции, создающие объекты (например, PyLong_FromLong()), уже делают это, поэтому данное замечание относится только к тем, кто вызывает malloc() напрямую.

Также обратите внимание, что, за важным исключением PyArg_ParseTuple() и друзей, функции, возвращающие целочисленный статус, обычно возвращают положительное значение или ноль в случае успеха и -1 в случае неудачи, как системные вызовы Unix.

И наконец, будьте осторожны, очищая мусор (делая вызовы Py_XDECREF() или Py_DECREF() для уже созданных объектов), когда вы возвращаете индикатор ошибки!

Выбор того, какое исключение вызывать, остается только за вами. Существуют предопределенные объекты C, соответствующие всем встроенным исключениям Python, например PyExc_ZeroDivisionError, которые вы можете использовать напрямую. Конечно, выбирать исключения нужно с умом - не используйте PyExc_TypeError для обозначения того, что файл не может быть открыт (скорее всего, это должно быть PyExc_OSError). Если что-то не так со списком аргументов, функция PyArg_ParseTuple() обычно вызывает PyExc_TypeError. Если у вас есть аргумент, значение которого должно находиться в определенном диапазоне или удовлетворять другим условиям, подойдет PyExc_ValueError.

Вы также можете определить новое исключение, уникальное для вашего модуля. Для этого обычно объявляют статическую переменную объекта в начале файла:

static PyObject *SpamError;

и инициализируйте его в функции инициализации вашего модуля (PyInit_spam()) с объектом исключения:

PyMODINIT_FUNC
PyInit_spam(void)
{
    PyObject *m;

    m = PyModule_Create(&spammodule);
    if (m == NULL)
        return NULL;

    SpamError = PyErr_NewException("spam.error", NULL, NULL);
    if (PyModule_AddObjectRef(m, "error", SpamError) < 0) {
        Py_CLEAR(SpamError);
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

Обратите внимание, что в Python объект исключения называется spam.error. Функция PyErr_NewException() может создать класс, базовым классом которого является Exception (если вместо NULL не передан другой класс), как описано в Встроенные исключения.

Обратите внимание, что переменная SpamError сохраняет ссылку на только что созданный класс исключения; это сделано намеренно! Поскольку исключение может быть удалено из модуля внешним кодом, необходимо сохранить ссылку на класс, чтобы гарантировать, что он не будет отброшен, в результате чего SpamError превратится в висячий указатель. Если он станет висячим указателем, код на языке C, который поднимает исключение, может вызвать дамп ядра или другие непредвиденные побочные эффекты.

Мы обсудим использование PyMODINIT_FUNC в качестве типа возврата функции позже в этом примере.

Исключение spam.error может быть вызвано в вашем модуле расширения с помощью вызова PyErr_SetString(), как показано ниже:

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = system(command);
    if (sts < 0) {
        PyErr_SetString(SpamError, "System command failed");
        return NULL;
    }
    return PyLong_FromLong(sts);
}

1.3. Вернуться к примеру

Возвращаясь к нашему примеру с функцией, вы теперь должны понимать следующее утверждение:

if (!PyArg_ParseTuple(args, "s", &command))
    return NULL;

Она возвращает NULL (индикатор ошибки для функций, возвращающих указатели объектов), если в списке аргументов обнаружена ошибка, полагаясь на исключение, заданное PyArg_ParseTuple(). В противном случае строковое значение аргумента было скопировано в локальную переменную command. Это присваивание указателя, и вы не должны изменять строку, на которую оно указывает (поэтому в стандартном Си переменная command должна быть правильно объявлена как const char *command).

Следующий оператор - это вызов функции Unix system(), передающий ей строку, которую мы только что получили из PyArg_ParseTuple():

sts = system(command);

Наша функция spam.system() должна вернуть значение sts в виде объекта Python. Для этого используется функция PyLong_FromLong().

return PyLong_FromLong(sts);

В данном случае он вернет целочисленный объект. (Да, в Python даже целые числа являются объектами в куче!)

Если у вас есть функция C, которая не возвращает ни одного полезного аргумента (функция, возвращающая void), то соответствующая функция Python должна возвращать None. Для этого вам понадобится следующая идиома (которая реализуется макросом Py_RETURN_NONE):

Py_INCREF(Py_None);
return Py_None;

Py_None - это имя на языке C для специального объекта Python None. Это настоящий объект Python, а не указатель NULL, который в большинстве контекстов, как мы видели, означает «ошибка».

1.4. Таблица методов модуля и функция инициализации

Я обещал показать, как вызывается spam_system() из программ на Python. Сначала нам нужно перечислить ее имя и адрес в «таблице методов»:

static PyMethodDef SpamMethods[] = {
    ...
    {"system",  spam_system, METH_VARARGS,
     "Execute a shell command."},
    ...
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

Обратите внимание на третью запись (METH_VARARGS). Это флаг, указывающий интерпретатору соглашение о вызове, которое будет использоваться для функции C. Обычно он всегда должен быть METH_VARARGS или METH_VARARGS | METH_KEYWORDS; значение 0 означает, что используется устаревший вариант PyArg_ParseTuple().

Если используется только METH_VARARGS, функция должна ожидать, что параметры уровня Python будут переданы в виде кортежа, приемлемого для разбора с помощью PyArg_ParseTuple(); более подробная информация об этой функции приведена ниже.

Бит METH_KEYWORDS может быть установлен в третьем поле, если в функцию должны передаваться аргументы в виде ключевых слов. В этом случае функция C должна принимать третий параметр PyObject *, который будет представлять собой словарь ключевых слов. Для разбора аргументов такой функции используйте PyArg_ParseTupleAndKeywords().

Таблица методов должна быть указана в структуре определения модуля:

static struct PyModuleDef spammodule = {
    PyModuleDef_HEAD_INIT,
    "spam",   /* name of module */
    spam_doc, /* module documentation, may be NULL */
    -1,       /* size of per-interpreter state of the module,
                 or -1 if the module keeps state in global variables. */
    SpamMethods
};

Эта структура, в свою очередь, должна быть передана интерпретатору в функции инициализации модуля. Функция инициализации должна иметь имя PyInit_name(), где name - имя модуля, и должна быть единственным неstatic элементом, определенным в файле модуля:

PyMODINIT_FUNC
PyInit_spam(void)
{
    return PyModule_Create(&spammodule);
}

Обратите внимание, что PyMODINIT_FUNC объявляет функцию с возвращаемым типом PyObject *, объявляет любые специальные объявления связей, требуемые платформой, и для C++ объявляет функцию как extern "C".

Когда программа Python впервые импортирует модуль spam, вызывается PyInit_spam(). (Комментарии о встраивании Python см. ниже). Он вызывает PyModule_Create(), который возвращает объект модуля, и вставляет объекты встроенных функций во вновь созданный модуль на основе таблицы (массива структур PyMethodDef), содержащейся в определении модуля. PyModule_Create() возвращает указатель на созданный им объект модуля. Она может прерваться с фатальной ошибкой при определенных ошибках или вернуть NULL, если модуль не удалось инициализировать удовлетворительно. Функция init должна вернуть объект модуля вызывающему ее пользователю, чтобы затем вставить его в sys.modules.

При встраивании Python функция PyInit_spam() не вызывается автоматически, если нет записи в таблице PyImport_Inittab. Чтобы добавить модуль в таблицу инициализации, используйте PyImport_AppendInittab(), за которой по желанию следует импорт модуля:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int
main(int argc, char *argv[])
{
    PyStatus status;
    PyConfig config;
    PyConfig_InitPythonConfig(&config);

    /* Add a built-in module, before Py_Initialize */
    if (PyImport_AppendInittab("spam", PyInit_spam) == -1) {
        fprintf(stderr, "Error: could not extend in-built modules table\n");
        exit(1);
    }

    /* Pass argv[0] to the Python interpreter */
    status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
    if (PyStatus_Exception(status)) {
        goto exception;
    }

    /* Initialize the Python interpreter.  Required.
       If this step fails, it will be a fatal error. */
    status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status)) {
        goto exception;
    }
    PyConfig_Clear(&config);

    /* Optionally import the module; alternatively,
       import can be deferred until the embedded script
       imports it. */
    PyObject *pmodule = PyImport_ImportModule("spam");
    if (!pmodule) {
        PyErr_Print();
        fprintf(stderr, "Error: could not import module 'spam'\n");
    }

    // ... use Python C API here ...

    return 0;

  exception:
     PyConfig_Clear(&config);
     Py_ExitStatusException(status);
}

Примечание

Удаление записей из sys.modules или импорт скомпилированных модулей в несколько интерпретаторов в рамках одного процесса (или после fork() без промежуточного exec()) может создать проблемы для некоторых модулей расширения. Авторам модулей расширения следует проявлять осторожность при инициализации внутренних структур данных.

Более подробный пример модуля включен в исходный дистрибутив Python под именем Modules/xxmodule.c. Этот файл можно использовать в качестве шаблона или просто прочитать как пример.

Примечание

В отличие от нашего примера spam, в xxmodule используется многофазная инициализация (новинка в Python 3.5), где структура PyModuleDef возвращается из PyInit_spam, а создание модуля остается на усмотрение механизма импорта. Подробнее о многофазной инициализации см. в PEP 489.

1.5. Компиляция и связывание

Прежде чем вы сможете использовать ваше новое расширение, нужно сделать еще две вещи: скомпилировать и связать его с системой Python. Если вы используете динамическую загрузку, детали могут зависеть от стиля динамической загрузки, используемого в вашей системе; более подробную информацию об этом см. в главах о сборке модулей расширения (глава Создание расширений на языках C и C++) и дополнительной информации, относящейся только к сборке под Windows (глава Создание расширений C и C++ в Windows).

Если вы не можете использовать динамическую загрузку или хотите сделать свой модуль постоянной частью интерпретатора Python, вам придется изменить конфигурационную настройку и перестроить интерпретатор. К счастью, на Unix это очень просто: достаточно поместить ваш файл (spammodule.c, например) в каталог Modules/ распакованного дистрибутива исходников, добавить в файл Modules/Setup.local строку с описанием вашего файла:

spam spammodule.o

и перестроить интерпретатор, выполнив make в каталоге toplevel. Вы также можете запустить make в подкаталоге Modules/, но тогда вам придется сначала перестроить Makefile, запустив „make Makefile“. (Это необходимо делать каждый раз, когда вы изменяете файл Setup).

Если вашему модулю требуются дополнительные библиотеки для компоновки, их тоже можно перечислить в строке конфигурационного файла, например:

spam spammodule.o -lX11

1.6. Вызов функций Python из C

До сих пор мы концентрировались на том, чтобы сделать функции C вызываемыми из Python. Обратное тоже полезно: вызов функций Python из C. Это особенно актуально для библиотек, поддерживающих так называемые функции «обратного вызова». Если интерфейс C использует обратные вызовы, то эквивалентный Python часто должен предоставить программисту Python механизм обратного вызова; реализация потребует вызова функций обратного вызова Python из функций обратного вызова C. Возможны и другие варианты использования.

К счастью, интерпретатор Python легко вызывается рекурсивно, и существует стандартный интерфейс для вызова функции Python. (Я не буду останавливаться на том, как вызвать парсер Python с определенной строкой на входе - если вам интересно, посмотрите на реализацию опции командной строки -c в Modules/main.c из исходного кода Python).

Вызвать функцию Python очень просто. Во-первых, программа Python должна каким-то образом передать вам объект функции Python. Вы должны предоставить функцию (или другой интерфейс) для этого. Когда эта функция будет вызвана, сохраните указатель на объект функции Python (будьте осторожны с Py_INCREF()!) в глобальной переменной — или там, где вы сочтете нужным. Например, следующая функция может быть частью определения модуля:

static PyObject *my_callback = NULL;

static PyObject *
my_set_callback(PyObject *dummy, PyObject *args)
{
    PyObject *result = NULL;
    PyObject *temp;

    if (PyArg_ParseTuple(args, "O:set_callback", &temp)) {
        if (!PyCallable_Check(temp)) {
            PyErr_SetString(PyExc_TypeError, "parameter must be callable");
            return NULL;
        }
        Py_XINCREF(temp);         /* Add a reference to new callback */
        Py_XDECREF(my_callback);  /* Dispose of previous callback */
        my_callback = temp;       /* Remember new callback */
        /* Boilerplate to return "None" */
        Py_INCREF(Py_None);
        result = Py_None;
    }
    return result;
}

Эта функция должна быть зарегистрирована в интерпретаторе с помощью флага METH_VARARGS; это описано в разделе Таблица методов модуля и функция инициализации. Функция PyArg_ParseTuple() и ее аргументы документированы в разделе Извлечение параметров в функциях расширения.

Макросы Py_XINCREF() и Py_XDECREF() увеличивают/уменьшают количество ссылок на объект и безопасны при наличии указателей NULL (но учтите, что temp не будет NULL в этом контексте). Подробнее о них в разделе Контрольные подсчеты.

Позже, когда придет время вызвать функцию, вы вызовете функцию C PyObject_CallObject(). У этой функции два аргумента, оба - указатели на произвольные объекты Python: сама функция Python и список аргументов. Список аргументов всегда должен быть кортежем, длина которого равна количеству аргументов. Чтобы вызвать функцию Python без аргументов, передайте NULL, или пустой кортеж; чтобы вызвать ее с одним аргументом, передайте кортеж singleton. Py_BuildValue() возвращает кортеж, если его строка формата состоит из нуля или более кодов формата, заключенных в круглые скобки. Например:

int arg;
PyObject *arglist;
PyObject *result;
...
arg = 123;
...
/* Time to call the callback */
arglist = Py_BuildValue("(i)", arg);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);

PyObject_CallObject() возвращает указатель на объект Python: это возвращаемое значение функции Python. PyObject_CallObject() является «ссылочно-нейтральной» по отношению к своим аргументам. В примере в качестве списка аргументов был создан новый кортеж, который Py_DECREF()- передается сразу после вызова PyObject_CallObject().

Возвращаемое значение PyObject_CallObject() является «новым»: либо это совершенно новый объект, либо это существующий объект, количество ссылок на который было увеличено. Поэтому, если вы не хотите сохранять его в глобальной переменной, вам следует каким-то образом Py_DECREF() результат, даже (особенно!) если его значение вас не интересует.

Однако прежде чем это сделать, необходимо убедиться, что возвращаемое значение не равно NULL. Если это так, то функция Python завершилась, вызвав исключение. Если код на языке C, вызвавший PyObject_CallObject(), вызывается из Python, он должен вернуть своему вызывающему коду Python признак ошибки, чтобы интерпретатор мог распечатать трассировку стека или вызывающий код Python мог обработать исключение. Если это невозможно или нежелательно, исключение должно быть устранено вызовом PyErr_Clear(). Например:

if (result == NULL)
    return NULL; /* Pass error back */
...use result...
Py_DECREF(result);

В зависимости от желаемого интерфейса для функции обратного вызова Python, вам может потребоваться также предоставить список аргументов для PyObject_CallObject(). В некоторых случаях список аргументов также предоставляется программой Python через тот же интерфейс, который задал функцию обратного вызова. Тогда его можно сохранить и использовать так же, как и объект функции. В других случаях может потребоваться создать новый кортеж для передачи в качестве списка аргументов. Самый простой способ сделать это - вызвать Py_BuildValue(). Например, если вы хотите передать интегральный код события, можно использовать следующий код:

PyObject *arglist;
...
arglist = Py_BuildValue("(l)", eventcode);
result = PyObject_CallObject(my_callback, arglist);
Py_DECREF(arglist);
if (result == NULL)
    return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);

Обратите внимание на размещение Py_DECREF(arglist) сразу после вызова, перед проверкой ошибки! Также обратите внимание, что, строго говоря, этот код не является полным: Py_BuildValue() может закончиться память, и это следует проверить.

Вы также можете вызвать функцию с аргументами в виде ключевых слов, используя PyObject_Call(), которая поддерживает аргументы и аргументы в виде ключевых слов. Как и в приведенном выше примере, для построения словаря мы используем Py_BuildValue().

PyObject *dict;
...
dict = Py_BuildValue("{s:i}", "name", val);
result = PyObject_Call(my_callback, NULL, dict);
Py_DECREF(dict);
if (result == NULL)
    return NULL; /* Pass error back */
/* Here maybe use the result */
Py_DECREF(result);

1.7. Извлечение параметров в функциях расширения

Функция PyArg_ParseTuple() объявляется следующим образом:

int PyArg_ParseTuple(PyObject *arg, const char *format, ...);

Аргумент arg должен быть кортежем, содержащим список аргументов, передаваемых из Python в функцию C. Аргумент format должен быть строкой формата, синтаксис которой объясняется в Разбор аргументов и создание значений в справочном руководстве Python/C API. Остальные аргументы должны быть адресами переменных, тип которых определяется строкой формата.

Обратите внимание, что хотя PyArg_ParseTuple() проверяет, что аргументы Python имеют требуемые типы, он не может проверить достоверность адресов переменных C, передаваемых в вызов: если вы допустите там ошибку, ваш код, вероятно, аварийно завершится или, по крайней мере, перезапишет случайные биты в памяти. Так что будьте осторожны!

Обратите внимание, что все ссылки на объекты Python, которые предоставляются вызывающей стороне, являются заимствованными ссылками; не уменьшайте количество их ссылок!

Некоторые примеры вызовов:

#define PY_SSIZE_T_CLEAN
#include <Python.h>
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;

ok = PyArg_ParseTuple(args, ""); /* No arguments */
    /* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
    /* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
    /* Possible Python call: f(1, 2, 'three') */
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
    /* A pair of ints and a string, whose size is also returned */
    /* Possible Python call: f((1, 2), 'three') */
{
    const char *file;
    const char *mode = "r";
    int bufsize = 0;
    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
    /* A string, and optionally another string and an integer */
    /* Possible Python calls:
       f('spam')
       f('spam', 'w')
       f('spam', 'wb', 100000) */
}
{
    int left, top, right, bottom, h, v;
    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
             &left, &top, &right, &bottom, &h, &v);
    /* A rectangle and a point */
    /* Possible Python call:
       f(((0, 0), (400, 300)), (10, 10)) */
}
{
    Py_complex c;
    ok = PyArg_ParseTuple(args, "D:myfunction", &c);
    /* a complex, also providing a function name for errors */
    /* Possible Python call: myfunction(1+2j) */
}

1.8. Параметры ключевых слов для функций расширения

Функция PyArg_ParseTupleAndKeywords() объявляется следующим образом:

int PyArg_ParseTupleAndKeywords(PyObject *arg, PyObject *kwdict,
                                const char *format, char * const *kwlist, ...);

Параметры arg и format идентичны параметрам функции PyArg_ParseTuple(). Параметр kwdict - это словарь ключевых слов, полученный в качестве третьего параметра от среды выполнения Python. Параметр kwlist - это NULL-конечный список строк, идентифицирующих параметры; имена сопоставляются с информацией о типе из format слева направо. В случае успеха PyArg_ParseTupleAndKeywords() возвращает true, в противном случае возвращает false и вызывает соответствующее исключение.

Примечание

Вложенные кортежи не могут быть разобраны при использовании аргументов с ключевыми словами! Переданные параметры ключевых слов, отсутствующие в kwlist, приведут к появлению TypeError.

Вот пример модуля, использующего ключевые слова, основанный на примере Geoff Philbrick (philbrick@hks.com):

#define PY_SSIZE_T_CLEAN
#include <Python.h>

static PyObject *
keywdarg_parrot(PyObject *self, PyObject *args, PyObject *keywds)
{
    int voltage;
    const char *state = "a stiff";
    const char *action = "voom";
    const char *type = "Norwegian Blue";

    static char *kwlist[] = {"voltage", "state", "action", "type", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i|sss", kwlist,
                                     &voltage, &state, &action, &type))
        return NULL;

    printf("-- This parrot wouldn't %s if you put %i Volts through it.\n",
           action, voltage);
    printf("-- Lovely plumage, the %s -- It's %s!\n", type, state);

    Py_RETURN_NONE;
}

static PyMethodDef keywdarg_methods[] = {
    /* The cast of the function is necessary since PyCFunction values
     * only take two PyObject* parameters, and keywdarg_parrot() takes
     * three.
     */
    {"parrot", (PyCFunction)(void(*)(void))keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
     "Print a lovely skit to standard output."},
    {NULL, NULL, 0, NULL}   /* sentinel */
};

static struct PyModuleDef keywdargmodule = {
    PyModuleDef_HEAD_INIT,
    "keywdarg",
    NULL,
    -1,
    keywdarg_methods
};

PyMODINIT_FUNC
PyInit_keywdarg(void)
{
    return PyModule_Create(&keywdargmodule);
}

1.9. Построение произвольных значений

Эта функция является аналогом PyArg_ParseTuple(). Она объявляется следующим образом:

PyObject *Py_BuildValue(const char *format, ...);

Она распознает набор единиц формата, аналогичных тем, что распознает PyArg_ParseTuple(), но аргументы (которые являются входными для функции, а не выходными) не должны быть указателями, а только значениями. Она возвращает новый объект Python, подходящий для возврата из функции C, вызываемой из Python.

Одно отличие от PyArg_ParseTuple(): если последний требует, чтобы его первый аргумент был кортежем (поскольку списки аргументов в Python всегда представляются как кортежи), то Py_BuildValue() не всегда строит кортеж. Он строит кортеж только в том случае, если его строка формата содержит две или более единиц формата. Если строка формата пуста, он возвращает None; если она содержит ровно одну единицу формата, он возвращает любой объект, описанный этой единицей формата. Чтобы заставить его возвращать кортеж размера 0 или 1, заключите строку формата в круглые скобки.

Примеры (слева - вызов, справа - результирующее значение Python):

Py_BuildValue("")                        None
Py_BuildValue("i", 123)                  123
Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)
Py_BuildValue("s", "hello")              'hello'
Py_BuildValue("y", "hello")              b'hello'
Py_BuildValue("ss", "hello", "world")    ('hello', 'world')
Py_BuildValue("s#", "hello", 4)          'hell'
Py_BuildValue("y#", "hello", 4)          b'hell'
Py_BuildValue("()")                      ()
Py_BuildValue("(i)", 123)                (123,)
Py_BuildValue("(ii)", 123, 456)          (123, 456)
Py_BuildValue("(i,i)", 123, 456)         (123, 456)
Py_BuildValue("[i,i]", 123, 456)         [123, 456]
Py_BuildValue("{s:i,s:i}",
              "abc", 123, "def", 456)    {'abc': 123, 'def': 456}
Py_BuildValue("((ii)(ii)) (ii)",
              1, 2, 3, 4, 5, 6)          (((1, 2), (3, 4)), (5, 6))

1.10. Контрольные подсчеты

В таких языках, как C или C++, программист отвечает за динамическое выделение и деаллокацию памяти в куче. В языке C для этого используются функции malloc() и free(). В C++ операторы new и delete используются практически с тем же смыслом, и мы ограничимся рассмотрением случая C.

Каждый блок памяти, выделенный с помощью malloc(), в конечном итоге должен быть возвращен в пул доступной памяти ровно одним вызовом free(). Важно вызывать free() в нужное время. Если адрес блока забыт, а free() для него не вызван, то занимаемая им память не может быть использована повторно до завершения работы программы. Это называется memory leak. С другой стороны, если программа вызывает free() для блока, а затем продолжает использовать этот блок, это создает конфликт с повторным использованием блока через другой вызов malloc(). Это называется using freed memory. Он имеет те же плохие последствия, что и обращение к неинициализированным данным - дампы ядра, неправильные результаты, загадочные сбои.

Частой причиной утечек памяти являются необычные пути в коде. Например, функция может выделить блок памяти, выполнить некоторые вычисления, а затем снова освободить этот блок. Теперь изменение требований к функции может добавить к вычислениям тест, который обнаруживает состояние ошибки и может преждевременно вернуть функцию. При таком преждевременном выходе легко забыть освободить выделенный блок памяти, особенно если он добавлен в код позже. Такие утечки, появившись однажды, часто остаются незамеченными в течение долгого времени: ошибочный выход происходит лишь в небольшой части всех вызовов, а большинство современных машин имеют большой объем виртуальной памяти, поэтому утечка становится заметной только в длительном процессе, который часто использует утекающую функцию. Поэтому важно предотвратить утечки, разработав соглашение или стратегию кодирования, которая минимизирует количество подобных ошибок.

Поскольку Python активно использует malloc() и free(), ему нужна стратегия, позволяющая избежать утечек памяти, а также использования освобожденной памяти. Выбранный метод называется reference counting. Принцип прост: каждый объект содержит счетчик, который увеличивается, когда ссылка на объект где-то хранится, и уменьшается, когда ссылка на него удаляется. Когда счетчик достигает нуля, последняя ссылка на объект удаляется и объект освобождается.

Альтернативная стратегия называется automatic garbage collection. (Иногда подсчет ссылок также называют стратегией сборки мусора, поэтому я использую слово «автоматическая», чтобы различать эти два понятия). Большим преимуществом автоматической сборки мусора является то, что пользователю не нужно явно вызывать free(). (Другим заявленным преимуществом является повышение скорости или улучшение использования памяти - однако это не является неопровержимым фактом). Недостатком является то, что для языка C не существует действительно переносимого автоматического сборщика мусора, в то время как подсчет ссылок может быть реализован переносимо (при условии наличия функций malloc() и free() - что стандарт C гарантирует). Возможно, когда-нибудь для языка C появится достаточно переносимый автоматический сборщик мусора. А пока нам придется жить с подсчетом ссылок.

Хотя Python использует традиционную реализацию подсчета ссылок, он также предлагает детектор циклов, который работает для обнаружения циклов ссылок. Это позволяет приложениям не беспокоиться о создании прямых или косвенных циклических ссылок, которые являются слабым местом сборки мусора, реализованной с использованием только подсчета ссылок. Циклы ссылок состоят из объектов, которые содержат (возможно, косвенные) ссылки на самих себя, так что каждый объект в цикле имеет ненулевой счетчик ссылок. Типичные реализации подсчета ссылок не могут освободить память, принадлежащую каким-либо объектам в цикле ссылок или ссылающуюся на объекты в цикле, даже если на сам цикл больше нет ссылок.

Детектор циклов способен обнаруживать мусорные циклы и восстанавливать их. Модуль gc предоставляет способ запуска детектора (функция collect()), а также интерфейсы конфигурации и возможность отключения детектора во время выполнения.

1.10.1. Подсчет ссылок в Python

Есть два макроса, Py_INCREF(x) и Py_DECREF(x), которые управляют увеличением и уменьшением количества ссылок. Py_DECREF() также освобождает объект, когда счетчик достигает нуля. Для гибкости он не вызывает free() напрямую - скорее, он делает вызов через указатель функции в type object объекта. Для этой (и других) целей каждый объект также содержит указатель на объект своего типа.

Теперь остается главный вопрос: когда использовать Py_INCREF(x) и Py_DECREF(x)? Давайте сначала введем некоторые термины. Никто не может «владеть» объектом; однако вы можете own a reference на него ссылаться. Количество ссылок на объект теперь определяется как количество принадлежащих ему ссылок. Владелец ссылки отвечает за вызов Py_DECREF(), когда ссылка больше не нужна. Владение ссылкой может быть передано. Существует три способа избавиться от принадлежащей ссылки: передать ее, сохранить или вызвать Py_DECREF(). Если забыть избавиться от принадлежащей ссылки, это приведет к утечке памяти.

Также можно borrow [2] ссылку на объект. Заемщик ссылки не должен вызывать Py_DECREF(). Заемщик не должен удерживать объект дольше, чем владелец, у которого он был заимствован. Использование заимствованной ссылки после того, как владелец избавился от нее, чревато использованием освобожденной памяти и должно полностью исключаться [3].

Преимущество заимствования перед владением ссылкой заключается в том, что вам не нужно заботиться об утилизации ссылки на всех возможных путях через код - другими словами, с заимствованной ссылкой вы не рискуете получить утечку при преждевременном выходе. Недостатком заимствования по сравнению с владением является то, что существуют некоторые тонкие ситуации, когда в казалось бы корректном коде заимствованная ссылка может быть использована после того, как владелец, у которого она была заимствована, на самом деле избавился от нее.

Заимствованную ссылку можно превратить в принадлежащую, вызвав команду Py_INCREF(). Это не влияет на статус владельца, у которого была заимствована ссылка, - это создает новую принадлежащую ссылку и дает полную ответственность владельца (новый владелец должен правильно распорядиться ссылкой, как и предыдущий владелец).

1.10.2. Правила владения

Всякий раз, когда ссылка на объект передается в функцию или из нее, спецификация интерфейса функции определяет, передается ли право собственности вместе с ссылкой или нет.

Большинство функций, возвращающих ссылку на объект, передают право собственности вместе со ссылкой. В частности, все функции, задачей которых является создание нового объекта, такие как PyLong_FromLong() и Py_BuildValue(), передают право собственности получателю. Даже если объект на самом деле не является новым, вы все равно получаете право собственности на новую ссылку на этот объект. Например, PyLong_FromLong() поддерживает кэш популярных значений и может возвращать ссылку на кэшированный элемент.

Многие функции, извлекающие объекты из других объектов, также передают право собственности вместе со ссылкой, например PyObject_GetAttrString(). Однако здесь картина не столь однозначна, поскольку несколько распространенных процедур являются исключениями: PyTuple_GetItem(), PyList_GetItem(), PyDict_GetItem() и PyDict_GetItemString() возвращают ссылки, которые вы заимствуете из кортежа, списка или словаря.

Функция PyImport_AddModule() также возвращает заимствованную ссылку, хотя на самом деле она может создать возвращаемый объект: это возможно потому, что принадлежащая ей ссылка на объект хранится в sys.modules.

Когда вы передаете ссылку на объект в другую функцию, в общем случае функция заимствует у вас эту ссылку - если ей нужно ее сохранить, она использует Py_INCREF(), чтобы стать независимым владельцем. Из этого правила есть ровно два важных исключения: PyTuple_SetItem() и PyList_SetItem(). Эти функции принимают на себя право собственности на переданный им элемент — даже если они потерпели неудачу! (Обратите внимание, что PyDict_SetItem() и ее друзья не становятся владельцами - они «обычные»).

Когда функция C вызывается из Python, она заимствует ссылки на свои аргументы у вызывающей стороны. Вызывающая функция владеет ссылкой на объект, поэтому время жизни заимствованной ссылки гарантировано до возвращения функции. Только когда такая заимствованная ссылка должна быть сохранена или передана, ее нужно превратить в принадлежащую ссылку, вызвав Py_INCREF().

Ссылка на объект, возвращаемая из функции C, которая вызывается из Python, должна быть ссылкой на владельца - право собственности передается от функции к ее вызывающему.

1.10.3. Тонкий лед

Есть несколько ситуаций, когда безобидное на первый взгляд использование заимствованной ссылки может привести к проблемам. Все они связаны с неявными вызовами интерпретатора, которые могут заставить владельца ссылки избавиться от нее.

Первый и самый важный случай, о котором следует знать, - это использование Py_DECREF() на несвязанном объекте при заимствовании ссылки на элемент списка. Например:

void
bug(PyObject *list)
{
    PyObject *item = PyList_GetItem(list, 0);

    PyList_SetItem(list, 1, PyLong_FromLong(0L));
    PyObject_Print(item, stdout, 0); /* BUG! */
}

Эта функция сначала заимствует ссылку на list[0], затем заменяет list[1] значением 0 и, наконец, печатает заимствованную ссылку. Выглядит безобидно, верно? Но это не так!

Давайте проследим за потоком управления в PyList_SetItem(). Список владеет ссылками на все свои элементы, поэтому при замене элемента 1 он должен избавиться от исходного элемента 1. Теперь предположим, что исходный элемент 1 был экземпляром пользовательского класса, и предположим, что этот класс определил метод __del__(). Если у этого экземпляра класса количество ссылок равно 1, то при его утилизации будет вызван его метод __del__().

Поскольку он написан на Python, метод __del__() может выполнить произвольный код Python. Может ли он сделать что-то, чтобы аннулировать ссылку на item в bug()? Конечно! Если предположить, что список, переданный в bug(), доступен методу __del__(), он может выполнить оператор del list[0], и если предположить, что это была последняя ссылка на этот объект, он освободит связанную с ним память, тем самым аннулировав item.

Решение, как только вы узнаете источник проблемы, простое: временно увеличьте счетчик ссылок. Правильная версия функции выглядит так:

void
no_bug(PyObject *list)
{
    PyObject *item = PyList_GetItem(list, 0);

    Py_INCREF(item);
    PyList_SetItem(list, 1, PyLong_FromLong(0L));
    PyObject_Print(item, stdout, 0);
    Py_DECREF(item);
}

Это правдивая история. Старая версия Python содержала варианты этой ошибки, и кто-то потратил значительное количество времени в отладчике на C, чтобы выяснить, почему его методы __del__() не работают…

Второй случай проблем с заимствованной ссылкой - это вариант, связанный с потоками. Обычно несколько потоков в интерпретаторе Python не могут мешать друг другу, поскольку существует глобальная блокировка, защищающая все объектное пространство Python. Однако можно временно снять эту блокировку с помощью макроса Py_BEGIN_ALLOW_THREADS, а затем снова снять ее с помощью Py_END_ALLOW_THREADS. Это часто происходит при блокировке вызовов ввода/вывода, чтобы позволить другим потокам использовать процессор в ожидании завершения ввода/вывода. Очевидно, что следующая функция имеет ту же проблему, что и предыдущая:

void
bug(PyObject *list)
{
    PyObject *item = PyList_GetItem(list, 0);
    Py_BEGIN_ALLOW_THREADS
    ...some blocking I/O call...
    Py_END_ALLOW_THREADS
    PyObject_Print(item, stdout, 0); /* BUG! */
}

1.10.4. Указатели NULL

В общем случае функции, принимающие в качестве аргументов ссылки на объекты, не ожидают, что вы передадите им указатели NULL, и будут сбрасывать ядро (или вызывать последующие сбрасывания ядра), если вы это сделаете. Функции, возвращающие ссылки на объекты, обычно возвращают NULL только для того, чтобы указать, что произошло исключение. Причина отказа от проверки аргументов NULL заключается в том, что функции часто передают полученные объекты другим функциям - если бы каждая функция проверяла NULL, было бы много лишних тестов, и код работал бы медленнее.

Лучше проверять наличие NULL только в «источнике:», когда указатель, который может быть NULL, получен, например, из malloc() или из функции, которая может вызвать исключение.

Макросы Py_INCREF() и Py_DECREF() не проверяют наличие указателей NULL - однако их варианты Py_XINCREF() и Py_XDECREF() проверяют.

Макросы для проверки определенного типа объекта (Pytype_Check()) не проверяют указатели NULL - опять же, существует много кода, который вызывает несколько таких макросов подряд для проверки объекта на различные ожидаемые типы, и это привело бы к появлению избыточных тестов. Вариантов с проверкой на NULL не существует.

Механизм вызова функций Си гарантирует, что список аргументов, передаваемый функциям Си (args в примерах), никогда не будет NULL. — фактически он гарантирует, что это всегда кортеж [4].

Это грубая ошибка - когда указатель NULL «убегает» к пользователю Python.

1.11. Написание расширений на C++

Можно писать модули расширения на C++. При этом действуют некоторые ограничения. Если основная программа (интерпретатор Python) скомпилирована и скомпонована компилятором C, глобальные или статические объекты с конструкторами не могут быть использованы. Это не является проблемой, если основная программа скомпилирована компилятором C++. Функции, которые будут вызываться интерпретатором Python (в частности, функции инициализации модулей), должны быть объявлены с использованием extern "C". Нет необходимости заключать заголовочные файлы Python в extern "C" {...}. — они и так используют эту форму, если определен символ __cplusplus (все последние компиляторы C++ определяют этот символ).

1.12. Предоставление API на языке C для модуля расширения

Многие модули расширения просто предоставляют новые функции и типы для использования из Python, но иногда код в модуле расширения может быть полезен для других модулей расширения. Например, модуль расширения может реализовать тип «коллекция», который работает как списки без порядка. Подобно тому, как стандартный тип списка в Python имеет C API, позволяющий модулям расширения создавать списки и манипулировать ими, этот новый тип коллекции должен иметь набор C функций для прямого манипулирования из других модулей расширения.

На первый взгляд, это просто: достаточно написать функции (разумеется, не объявляя их static), предоставить соответствующий заголовочный файл и задокументировать C API. И на самом деле это работало бы, если бы все модули расширения всегда статически связывались с интерпретатором Python. Однако когда модули используются в качестве разделяемых библиотек, символы, определенные в одном модуле, могут быть не видны другому модулю. Детали видимости зависят от операционной системы; некоторые системы используют одно глобальное пространство имен для интерпретатора Python и всех модулей расширения (Windows, например), тогда как другие требуют явного списка импортируемых символов во время компоновки модуля (AIX - один из примеров), или предлагают выбор различных стратегий (большинство Unices). И даже если символы видны глобально, модуль, функции которого вы хотите вызвать, может быть еще не загружен!

Поэтому переносимость требует не делать никаких предположений о видимости символов. Это означает, что все символы в модулях расширения должны быть объявлены static, за исключением функции инициализации модуля, чтобы избежать столкновений имен с другими модулями расширения (как обсуждалось в разделе Таблица методов модуля и функция инициализации). И это означает, что символы, которые должны быть доступны из других модулей расширения, должны экспортироваться другим способом.

Python предоставляет специальный механизм для передачи информации уровня C (указателей) из одного модуля расширения в другой: Капсулы. Капсула - это тип данных Python, который хранит указатель (void*). Капсулы можно создавать и обращаться к ним только через их C API, но их можно передавать, как любой другой объект Python. В частности, им можно присвоить имя в пространстве имен модуля расширения. Другие модули расширения могут импортировать этот модуль, получить значение этого имени, а затем извлечь указатель из капсулы.

Существует множество способов использования капсул для экспорта C API модуля расширения. Каждая функция может получить свой собственный Capsule, или все указатели C API могут храниться в массиве, адрес которого опубликован в Capsule. А различные задачи по хранению и получению указателей могут быть по-разному распределены между модулем, предоставляющим код, и клиентскими модулями.

Какой бы метод вы ни выбрали, важно правильно назвать ваши капсулы. Функция PyCapsule_New() принимает параметр имени (const char*); вы можете передать имя NULL, но мы настоятельно рекомендуем вам указать имя. Правильно названные капсулы обеспечивают определенную безопасность типов во время выполнения; нет никакого реального способа отличить одну безымянную капсулу от другой.

В частности, капсулы, используемые для демонстрации API на языке C, должны иметь имя, соответствующее этому соглашению:

modulename.attributename

Удобная функция PyCapsule_Import() позволяет легко загрузить C API, предоставленный через капсулу, но только если имя капсулы соответствует этому соглашению. Такое поведение дает пользователям C API высокую степень уверенности в том, что загружаемая ими капсула содержит правильный C API.

Следующий пример демонстрирует подход, который возлагает большую часть нагрузки на автора экспортируемого модуля, что подходит для часто используемых библиотечных модулей. Он хранит все указатели C API (в примере только один!) в массиве указателей void, который становится значением капсулы. В заголовочном файле, соответствующем модулю, содержится макрос, который позаботится об импорте модуля и получении его указателей C API; клиентским модулям остается только вызвать этот макрос перед обращением к C API.

Экспортирующий модуль является модификацией модуля spam из раздела Простой пример. Функция spam.system() вызывает не библиотечную функцию system() напрямую, а функцию PySpam_System(), которая, конечно, в реальности будет делать что-то более сложное (например, добавлять «spam» к каждой команде). Эта функция PySpam_System() также экспортируется в другие модули расширения.

Функция PySpam_System() - это обычная функция C, объявленная static, как и все остальное:

static int
PySpam_System(const char *command)
{
    return system(command);
}

Функция spam_system() изменяется тривиальным образом:

static PyObject *
spam_system(PyObject *self, PyObject *args)
{
    const char *command;
    int sts;

    if (!PyArg_ParseTuple(args, "s", &command))
        return NULL;
    sts = PySpam_System(command);
    return PyLong_FromLong(sts);
}

В начале модуля, сразу после строки

#include <Python.h>

необходимо добавить еще две строки:

#define SPAM_MODULE
#include "spammodule.h"

Символ #define используется для того, чтобы сообщить заголовочному файлу, что он включается в экспортируемый модуль, а не в клиентский модуль. Наконец, функция инициализации модуля должна позаботиться об инициализации массива указателей C API:

PyMODINIT_FUNC
PyInit_spam(void)
{
    PyObject *m;
    static void *PySpam_API[PySpam_API_pointers];
    PyObject *c_api_object;

    m = PyModule_Create(&spammodule);
    if (m == NULL)
        return NULL;

    /* Initialize the C API pointer array */
    PySpam_API[PySpam_System_NUM] = (void *)PySpam_System;

    /* Create a Capsule containing the API pointer array's address */
    c_api_object = PyCapsule_New((void *)PySpam_API, "spam._C_API", NULL);

    if (PyModule_Add(m, "_C_API", c_api_object) < 0) {
        Py_DECREF(m);
        return NULL;
    }

    return m;
}

Обратите внимание, что PySpam_API объявлен static; иначе массив указателей исчез бы при завершении PyInit_spam()!

Основная часть работы находится в заголовочном файле spammodule.h, который выглядит следующим образом:

#ifndef Py_SPAMMODULE_H
#define Py_SPAMMODULE_H
#ifdef __cplusplus
extern "C" {
#endif

/* Header file for spammodule */

/* C API functions */
#define PySpam_System_NUM 0
#define PySpam_System_RETURN int
#define PySpam_System_PROTO (const char *command)

/* Total number of C API pointers */
#define PySpam_API_pointers 1


#ifdef SPAM_MODULE
/* This section is used when compiling spammodule.c */

static PySpam_System_RETURN PySpam_System PySpam_System_PROTO;

#else
/* This section is used in modules that use spammodule's API */

static void **PySpam_API;

#define PySpam_System \
 (*(PySpam_System_RETURN (*)PySpam_System_PROTO) PySpam_API[PySpam_System_NUM])

/* Return -1 on error, 0 on success.
 * PyCapsule_Import will set an exception if there's an error.
 */
static int
import_spam(void)
{
    PySpam_API = (void **)PyCapsule_Import("spam._C_API", 0);
    return (PySpam_API != NULL) ? 0 : -1;
}

#endif

#ifdef __cplusplus
}
#endif

#endif /* !defined(Py_SPAMMODULE_H) */

Все, что должен сделать клиентский модуль, чтобы получить доступ к функции PySpam_System(), - это вызвать функцию (или, скорее, макрос) import_spam() в своей функции инициализации:

PyMODINIT_FUNC
PyInit_client(void)
{
    PyObject *m;

    m = PyModule_Create(&clientmodule);
    if (m == NULL)
        return NULL;
    if (import_spam() < 0)
        return NULL;
    /* additional initialization can happen here */
    return m;
}

Главный недостаток этого подхода заключается в том, что файл spammodule.h довольно сложен. Однако базовая структура одинакова для каждой экспортируемой функции, поэтому ее нужно выучить только один раз.

Наконец, следует упомянуть, что капсулы предлагают дополнительную функциональность, которая особенно полезна для выделения и удаления памяти указателя, хранящегося в капсуле. Подробности описаны в справочном руководстве по API Python/C в разделе Капсулы и в реализации Capsules (файлы Include/pycapsule.h и Objects/pycapsule.c в дистрибутиве исходного кода Python).

Сноски