Все необходимые для модуля расширения определения находятся в заголовочном файле Python.h, который должен находится где–то на пути заголовочных файлов компилятора C/C++. Следует пользоваться теми же версиями библиотек, с которыми был откомпилирован Python. Желательно, и той же маркой компилятора C/C++.
Связь с интерпретатором Python из кода на C осуществляется путем вызова функций, определенных в интерпретаторе Python. Все функции начинаются на Py или _Py, потому во избежание конфликтов в модулях расширения не следует определять функций с подобными именами.
Через C API доступны все встроенные возможности языка Python (при необходимости, детальнее изучить этот вопрос можно по документации):
высокоуровневый интерфейс интерпретатора (функции и макросы Py_Main(), PyRun_String(), PyRun_File(), Py_CompileString(), PyCompilerFlags() и т.п.),
функции для работы со встроенным интерпретатором и потоками (Py_Initialize(), Py_Finalize(), Py_NewInterpreter(), Py_EndInterpreter(), Py_SetProgramName() и другие),
управление подсчетом ссылок (макросы Py_INCREF(), Py_DECREF(), Py_XINCREF(), Py_XDECREF(), Py_CLEAR()). Требуется при создании или удалении Python–объектов в C/C++-коде.
обработка исключений (PyErr*-функции и PyExc_*-константы, например, PyErr_NoMemory() и PyExc_IOError)
управление процессом и сервисы операционной системы (Py_FatalError(), Py_Exit(), Py_AtExit(), PyOS_CheckStack(), и другие функции/макросы PyOS*),
импорт модулей (PyImport_Import() и другие),
поддержка сериализации объектов (PyMarshal_WriteObjectToFile(), PyMarshal_ReadObjectFromFile() и т.п.)
поддержка анализа строки аргументов (PyArg_ParseTuple(), PyArg_VaParse(), PyArg_ParseTupleAndKeywords(), PyArg_VaParseTupleAndKeywords(), PyArg_UnpackTuple() и Py_BuildValue()). С помощью этих функций облегчается задача получения в коде на C параметров, заданных при вызове функции из Python. Функции PyArg_Parse* принимают в качестве аргумента строку формата полученных аргументов,
поддержка протоколов абстрактных объектов: + Протокол объекта (PyObject_Print(), PyObject_HasAttrString(), PyObject_GetAttrString(), PyObject_HasAttr(), PyObject_GetAttr(), PyObject_RichCompare(), …, PyObject_IsInstance(), PyCallable_Check(), PyObject_Call(), PyObject_Dir() и другие). То, что должен уметь делать любой объект Python + Протокол числа (PyNumber_Check(), PyNumber_Add(), …, PyNumber_And(), …, PyNumber_InPlaceAdd(), …, PyNumber_Coerce(), PyNumber_Int(), …). То, что должен делать любой объект, представляющий число + Протокол последовательности (PySequence_Check(), PySequence_Size(), PySequence_Concat(), PySequence_Repeat(), PySequence_InPlaceConcat(), …, PySequence_GetItem(), …, PySequence_GetSlice(), PySequence_Tuple(), PySequence_Count(), …) + Протокол отображения (например, словарь является отображением) (функции: PyMapping_Check(), PyMapping_Length(), PyMapping_HasKey(), PyMapping_Keys(), …, PyMapping_SetItemString(), PyMapping_GetItemString() и др.) + Протокол итератора (PyIter_Check(), PyIter_Next()) + Протокол буфера (PyObject_AsCharBuffer(), PyObject_AsReadBuffer(), PyObject_AsWriteBuffer(), PyObject_CheckReadBuffer())
поддержка встроенных типов данных. Аналогично описанному в предыдущем пункте, но уже для конкретных встроенных типов данных. Например: + Булевский объект (PyBool_Check() - проверка принадлежности типу PyBool_Type, Py_False — объект False, Py_True — объект True,
управление памятью (то есть кучей интерпретатора Python) (функции PyMem_Malloc(), PyMem_Realloc(), PyMem_Free(), PyMem_New(), PyMem_Resize(), PyMem_Del()). Разумеется, можно применять и средства выделения памяти C/C++, однако, в этом случае не будут использоваться преимущества управления памятью интерпретатора Python (сборка мусора и т.п.). Кроме того, освобождение памяти нужно производить тем же способом, что и ее выделение. Еще раз стоит напомнить, что повторное освобождение одной и той же области памяти (а равно использование области памяти после ее освобождения) чревато серьезными ошибками, которые компилятор C не имеет возможности распознать.
структуры для определения объектов встроенных типов (PyObject, PyVarObject и много других)
Примечание
Под протоколом здесь понимается набор методов, которые должен поддерживать тот или иной класс для организации операций со своими экземплярами. Эти методы доступны не только из Python (например, len(a) дает длину последовательности), но и из кода на C (PySequence_Length()).
Написание модуля расширения
Если необходимость встроить Python в программу возникает нечасто, то его расширение путем написания модулей на C/C++ - довольно распространенная практика. Изначально Python был нацелен на возможность расширения, поэтому в настоящий момент очень многие C/C++-библиотеки имеют привязки к Python.
Привязка к Python, хотя и может быть несколько автоматизирована, все же это процесс творческий. Дело в том, что если предполагается интенсивно использовать библиотеку в Python, ее привязку желательно сделать как можно более тщательно. Возможно, в ходе привязки будет сделана объектно–ориентированная надстройка или другие архитектурные изменения, которые позволят упростить использование библиотеки.
В качестве примера можно привести выдержку из исходного кода модуля md5, который реализует функцию для получения md5–дайджеста. Модуль приводится в целях иллюстрации (то есть, с сокращениями). Модуль вводит собственный тип данных, MD5Type, поэтому можно увидеть не только реализацию функций, но и способ описания встроенного типа. В рамках этого курса не изучить все тонкости программирования модулей расширения, главное понять дух этого занятия. На комментарии автора курса лекций указывает двойной слэш //:
Листинг
// заголовочные файлы
#include «Python.h»
#include «md5.h»
// В частности, в заголовочном файле md5.h есть следующие определения:
// typedef unsigned char *POINTER;
// typedef unsigned int UINT4;
// typedef struct {
// UINT4 state[4]; /* state (ABCD) */
// UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
// unsigned char buffer[64]; /* input buffer */
// } MD5_CTX;
// Структура объекта MD5type
typedef struct {
PyObject_HEAD
MD5_CTX md5; /* the context holder */
} md5object;
// Определение типа объекта MD5type
static PyTypeObject MD5type;
// Макрос проверки типа MD5type
#define is_md5object(v) ((v) — >ob_type == &MD5type)
// Порождение объекта типа MD5type
static md5object *
newmd5object(void)
{
md5object *md5p;
md5p = PyObject_New(md5object, &MD5type);
if (md5p == NULL)
return NULL; // не хватило памяти
MD5Init(&md5p->md5); // инициализация
return md5p;
}
// Определения методов
// Освобождение памяти из–под объекта
static void
md5_dealloc(md5object *md5p) { PyObject_Del(md5p); }
static PyObject *
md5_update(md5object *self, PyObject *args)
{
unsigned char *cp;
int len;
// разбор строки аргументов. Формат указывает следующее:
// s# - один параметр, строка (заданная указателем и длиной)
// : — разделитель
// update — название метода
if (!PyArg_ParseTuple(args, «s#:update», &cp, &len))
return NULL;
MD5Update(&self->md5, cp, len);
// Даже возврат None требует увеличения счетчика ссылок
Py_INCREF(Py_None);
return Py_None;
}