Учебники

Python 3 — программирование расширений на C

Любой код, который вы пишете с использованием любого скомпилированного языка, такого как C, C ++ или Java, может быть интегрирован или импортирован в другой скрипт Python. Этот код считается «расширением».

Модуль расширения Python — это не более чем обычная библиотека Си. На машинах Unix эти библиотеки обычно заканчиваются на .so (для общего объекта). На компьютерах с Windows вы обычно видите .dll (для динамически связанной библиотеки).

Предварительные условия для написания расширений

Чтобы начать писать свое расширение, вам понадобятся заголовочные файлы Python.

  • На Unix-машинах это обычно требует установки пакета для разработчика, такого как python2.5-DEV ,

  • Пользователи Windows получают эти заголовки как часть пакета, когда они используют двоичный установщик Python.

На Unix-машинах это обычно требует установки пакета для разработчика, такого как python2.5-DEV ,

Пользователи Windows получают эти заголовки как часть пакета, когда они используют двоичный установщик Python.

Кроме того, предполагается, что вы хорошо знаете C или C ++, чтобы написать любое расширение Python с использованием программирования на C.

Сначала посмотрите на расширение Python

Для первого взгляда на модуль расширения Python вам нужно сгруппировать код в четыре части —

  • Заголовочный файл Python.h .

  • Функции C, которые вы хотите использовать в качестве интерфейса вашего модуля.

  • Таблица, отображающая имена ваших функций, которые разработчики Python видят как функции C внутри модуля расширения.

  • Функция инициализации.

Заголовочный файл Python.h .

Функции C, которые вы хотите использовать в качестве интерфейса вашего модуля.

Таблица, отображающая имена ваших функций, которые разработчики Python видят как функции C внутри модуля расширения.

Функция инициализации.

Заголовочный файл Python.h

Вам необходимо включить заголовочный файл Python.h в ваш исходный файл C, который дает вам доступ к внутреннему API Python, используемому для подключения вашего модуля к интерпретатору.

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

Функции C

Сигнатуры C-реализации ваших функций всегда принимают одну из следующих трех форм:

static PyObject *MyFunction( PyObject *self, PyObject *args );

static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw);

static PyObject *MyFunctionWithNoArgs( PyObject *self );

Каждое из предыдущих объявлений возвращает объект Python. В Python нет такой вещи, как void- функция, как в C. Если вы не хотите, чтобы ваши функции возвращали значение, верните C-эквивалент значения None в Python. Заголовки Python определяют макрос Py_RETURN_NONE, который делает это для нас.

Имена ваших функций C могут быть любыми, поскольку они никогда не видны за пределами модуля расширения. Они определены как статическая функция.

Ваши функции C обычно называются путем объединения модуля Python и имен функций вместе, как показано здесь —

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

Это функция Python, называемая func внутри модуля module . Вы будете помещать указатели на свои функции C в таблицу методов для модуля, который обычно идет следующим в вашем исходном коде.

Таблица сопоставления методов

Эта таблица методов представляет собой простой массив структур PyMethodDef. Эта структура выглядит примерно так —

struct PyMethodDef {
   char *ml_name;
   PyCFunction ml_meth;
   int ml_flags;
   char *ml_doc;
};

Вот описание членов этой структуры —

  • ml_name — это имя функции, которое представляет интерпретатор Python, когда оно используется в программах Python.

  • ml_meth — это адрес функции с любой из сигнатур, описанных в предыдущем разделе.

  • ml_flags — сообщает интерпретатору, какая из трех сигнатур используется ml_meth.

    • Этот флаг обычно имеет значение METH_VARARGS.

    • Этот флаг может быть побитовым ИЛИ с METH_KEYWORDS, если вы хотите разрешить ключевые аргументы в вашей функции.

    • Это также может иметь значение METH_NOARGS, которое указывает, что вы не хотите принимать какие-либо аргументы.

  • ml_doc — это строка документации для функции, которая может быть NULL, если вы не хотите писать ее.

ml_name — это имя функции, которое представляет интерпретатор Python, когда оно используется в программах Python.

ml_meth — это адрес функции с любой из сигнатур, описанных в предыдущем разделе.

ml_flags — сообщает интерпретатору, какая из трех сигнатур используется ml_meth.

Этот флаг обычно имеет значение METH_VARARGS.

Этот флаг может быть побитовым ИЛИ с METH_KEYWORDS, если вы хотите разрешить ключевые аргументы в вашей функции.

Это также может иметь значение METH_NOARGS, которое указывает, что вы не хотите принимать какие-либо аргументы.

ml_doc — это строка документации для функции, которая может быть NULL, если вы не хотите писать ее.

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

пример

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

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

Функция инициализации

Последняя часть вашего модуля расширения — это функция инициализации. Эта функция вызывается интерпретатором Python при загрузке модуля. Требуется, чтобы функция называлась init Module , где Module — это имя модуля.

Функция инициализации должна быть экспортирована из библиотеки, которую вы будете собирать. Заголовки Python определяют PyMODINIT_FUNC, чтобы включить соответствующие заклинания для того, чтобы это произошло для конкретной среды, в которой мы компилируем. Все, что вам нужно сделать, это использовать его при определении функции.

Ваша функция инициализации C обычно имеет следующую общую структуру:

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

Вот описание функции Py_InitModule3

  • func — это функция для экспорта.

  • module_methods — это имя таблицы сопоставления, определенное выше.

  • docstring — это комментарий, который вы хотите оставить в своем расширении.

func — это функция для экспорта.

module_methods — это имя таблицы сопоставления, определенное выше.

docstring — это комментарий, который вы хотите оставить в своем расширении.

Собрав все это вместе, это выглядит следующим образом —

#include <Python.h>

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

пример

Простой пример, который использует все вышеупомянутые понятия —

#include <Python.h>

static PyObject* helloworld(PyObject* self)
{
   return Py_BuildValue("s", "Hello, Python extensions!!");
}

static char helloworld_docs[] =
   "helloworld( ): Any message you want to put here!!\n";

static PyMethodDef helloworld_funcs[] = {
   {"helloworld", (PyCFunction)helloworld, 
   METH_NOARGS, helloworld_docs},
   {NULL}
};

void inithelloworld(void)
{
   Py_InitModule3("helloworld", helloworld_funcs, "Extension module example!");
}

Здесь функция Py_BuildValue используется для создания значения Python. Сохраните приведенный выше код в файле hello.c Мы увидим, как скомпилировать и установить этот модуль для вызова из скрипта Python.

Сборка и установка расширений

Пакет distutils позволяет очень легко распространять модули Python, как чистый Python, так и модули расширения, стандартным способом. Модули распространяются в исходной форме, собираются и устанавливаются с помощью сценария установки, обычно называемого setup.py as.

Для вышеуказанного модуля вам нужно подготовить следующий скрипт setup.py —

from distutils.core import setup, Extension
setup(name = 'helloworld', version = '1.0',  \
   ext_modules = [Extension('helloworld', ['hello.c'])])

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

$ python setup.py install

В системах на основе Unix вам, скорее всего, нужно будет запустить эту команду от имени пользователя root, чтобы иметь права на запись в каталог site-packages. Обычно это не проблема для Windows.

Импорт расширений

Как только вы установите свои расширения, вы сможете импортировать и вызывать это расширение в своем скрипте Python следующим образом:

пример

#!/usr/bin/python3
import helloworld

print helloworld.helloworld()

Выход

Это даст следующий результат —

Hello, Python extensions!!

Передача параметров функции

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

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Parse args and do something interesting here. */
   Py_RETURN_NONE;
}

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

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { "func", module_func, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
};

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

Первый аргумент PyArg_ParseTuple — аргумент args. Это объект, который вы будете анализировать . Второй аргумент — это строка формата, описывающая аргументы в ожидаемом порядке. Каждый аргумент представлен одним или несколькими символами в строке формата следующим образом.

static PyObject *module_func(PyObject *self, PyObject *args) {
   int i;
   double d;
   char *s;

   if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
      return NULL;
   }
   
   /* Do something interesting here. */
   Py_RETURN_NONE;
}

Выход

Компилируя новую версию вашего модуля и импортируя ее, вы можете вызывать новую функцию с любым количеством аргументов любого типа —

module.func(1, s = "three", d = 2.0)
module.func(i = 1, d = 2.0, s = "three")
module.func(s = "three", d = 2.0, i = 1)

Вы можете, вероятно, придумать еще больше вариантов.

Функция PyArg_ParseTuple

Вот стандартная подпись для функции PyArg_ParseTuple

int PyArg_ParseTuple(PyObject* tuple,char* format,...)

Эта функция возвращает 0 для ошибок и значение, не равное 0 для успеха. Tuple — это PyObject *, который был вторым аргументом функции C. Здесь формат представляет собой строку C, которая описывает обязательные и необязательные аргументы.

Вот список кодов формата для функции PyArg_ParseTuple

Код Тип C Имея в виду
с голец Строка Python длиной 1 становится символом C.
d двойной Поплавок Python становится двойным Си.
е поплавок Поплавок Python становится поплавком Си.
я ИНТ Python int становится C int.
L долго Int Python становится C длиной.
L долго долго Python int становится C long long
О PyObject * Получает ненулевую заимствованную ссылку на аргумент Python.
s символ * Строка Python без встроенных нулей в C char *.
s # символ * + INT Любая строка Python для адреса и длины C.
т # символ * + INT Доступный только для чтения односегментный буфер для адреса C и длины.
U Py_UNICODE * Python Unicode без встроенных нулей в C.
U # Py_UNICODE * + INT Любой Python Unicode C адрес и длина.
ш # символ * + INT Чтение / запись односегментного буфера на C адрес и длину.
Z символ * Как и s, также принимает None (устанавливает C char * в NULL).
г # символ * + INT Как и s #, также принимает None (устанавливает C char * в NULL).
(…) согласно … Последовательность Python рассматривается как один аргумент для каждого элемента.
| Следующие аргументы являются необязательными.
: Формат конца, а затем имя функции для сообщений об ошибках.
; Конец формата с последующим полным текстом сообщения об ошибке.

Возвращаемые значения

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

static PyObject *foo_add(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("i", a + b);
}

Вот как это будет выглядеть, если реализовано в Python —

def add(a, b):
   return (a + b)

Вы можете вернуть два значения из вашей функции следующим образом. Это будет записано с использованием списка в Python.

static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("ii", a + b, a - b);
}

Вот как это будет выглядеть, если реализовано в Python —

def add_subtract(a, b):
   return (a + b, a - b)

Функция Py_BuildValue

Вот стандартная подпись для функции Py_BuildValue

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

Здесь формат представляет собой строку C, которая описывает объект Python для построения. Следующие аргументы Py_BuildValue являются значениями C, из которых строится результат. Результат PyObject * является новой ссылкой.

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

Код Тип C Имея в виду
с голец AC char становится строкой Python длиной 1.
d двойной AC double становится поплавком Python.
е поплавок AC float становится Python float.
я ИНТ AC int становится Python int.
L долго AC долго становится Python Int.
N PyObject * Пропускает объект Python и крадет ссылку.
О PyObject * Проходит объект Python и INCREFs его как обычно.
O & конвертировать + аннулируются * Произвольное преобразование
s символ * C 0 заканчивается символом * в строке Python или NULL для None.
s # символ * + INT C char * и длина до строки Python или NULL до None.
U Py_UNICODE * C-широкая строка с нулевым символом в конце для Python Unicode или NULL для None.
U # Py_UNICODE * + INT C-строка и длина в Python Unicode или NULL в None.
ш # символ * + INT Чтение / запись односегментного буфера на C адрес и длину.
Z символ * Как и s, также принимает None (устанавливает C char * в NULL).
г # символ * + INT Как и s #, также принимает None (устанавливает C char * в NULL).
(…) согласно … Создает кортеж Python из значений C.
[…] согласно … Создает список Python из значений C.
{…} согласно … Создает словарь Python из значений C, чередующихся ключей и значений.

Код {…} создает словари из четного числа значений C, поочередно ключей и значений. Например, Py_BuildValue («{issi}», 23, «zig», «zag», 42) возвращает словарь, подобный Python {23: ‘zig’, ‘zag’: 42}.