Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Как интегрировать сценарий на языке Python в программу на языке C

Оригинал: How to embed Python code in C program
Автор: Muhammad Usman Nasir
Дата публикации: 4 декабря 2016 г.
Перевод: А.Панин
Дата перевода: 13 декабря 2016 г.

Язык программирования Python завоевал популярность среди разработчиков благодаря своему простому синтаксису, низкому порогу вхождения и поддержке множества аппаратных платформ. Благодаря большому количеству доступных библиотек и модулей у разработчиков имеется возможность реализации сложных функций программ буквально в рамках нескольких строк кода. Это обстоятельство делает язык программирования Python одним из наиболее продуктивных инструментов для разработки прототипов. Однако, язык Python не является таким быстрым, как язык C, поэтому многие высокопроизводительные программные компоненты для промышленной эксплуатации, такие, как ядро Linux, различные веб-серверы и серверы баз данных разрабатываются с использованием языка программирования C. Если вы разрабатываете программу на языке C, причем часть этой программы должна быть разработана на языке Python, вы можете разработать модуль Python и интегрировать его в свою программу на языке C с помощью специального API Python/C.

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

Шаг 1: Установка пакета с компонентами для разработки приложений, взаимодействующих с интерпретатором Python

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

В дистрибутивах Debian, Ubuntu и Linux Mint для этой цели может использоваться команда:

$ sudo apt-get install python2.7-dev

В дистрибутивах CentOS, Fedora и RHEL - следующая команда:

$ sudo yum install python-devel

После успешного окончания процесса установки заголовочный файл библиотеки интерпретатора Python будет размещен в директории /usr/include/python2.7. В зависимости от используемого дистрибутива Linux, путь к директории с этим файлом может немного отличаться. Например, в CentOS 6 он будет размещен в директории /usr/include/python2.6.

Шаг 2: Инициализация интерпретатора и установка пути к директории с модулем

Первым шагом на пути к интеграции сценария на языке Python в код программы на языке C является инициализация интерпретатора Python, которая осуществляется путем вызова следующей функции из программы:

Py_Initialize();

После того, как интерпретатор инициализирован, вам придется установить путь к директории с модулем, который вы планируете импортировать с помощью вашей программы на языке C. Например, предположим, что ваш модуль Python расположен в директории /usr/local/modules. В этом случае для установки пути к директории с модулем вы можете осуществить вызов следующей функции из программы:

PySys_SetPath("/usr/local/modules");

Шаг 3: Преобразование типов данных

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

PyObject *pval;
char *cString = "Cyberpersons";
pval = PyString_FromString(cString);

Другой аналогичной функцией является функция PyInt_FromLong(), которая позволяет преобразовать данные типа long языка C в данные типа int языка Python. Каждая функция API Python/C возвращает указатель на переменную типа PyObject.

Шаг 4: Разработка модуля Python

Если вы хотите интегрировать код на языке Python в код на другом языке программирования, таком, как C, вам придется разработать модуль Python, который впоследствии будет "импортирован" из программы на другом языке программирования. По этой причине следует уделить время рассмотрению методики импортирования модуля Python в программу на языке C.

В качестве примера я предлагаю реализовать простой модуль Python со следующим кодом:

def printData(data):
    return data+data+'\n'

Приведенная выше функция принимает один строковый аргумент и возвращает два повтора переданной строки. Например, если вы передадите в данную функцию строку "cyberpersons", эта функция вернет строку "cyberpersonscyberpersons". Сохраните этот код в файле с именем "printData.py" и разместите получившийся модуль Python в заданной ранее директории для модулей (/usr/local/modules).

Шаг 5: Загрузка модуля Python

Теперь, когда вы создали модуль Python, пришло время загрузить его на уровне программы на языке C. Код на языке C для импорта модуля будет выглядеть аналогичным образом:

// argv[1] хранит имя файла модуля ("printData.py").
pName = PyString_FromString(argv[1]);
pModule = PyImport_Import(pName);

Шаг 6: Формирование набора аргументов функций

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

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

PyObject *pythonArgument;
pythonArgument = PyTuple_New(1);
pValue = PyString_FromString(argv[3]);
 
if(pValue==NULL){
  return 1;
}
PyTuple_SetItem(pythonArgument, 0, pValue);

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

Шаг 7: Вызов функция модуля Python

После успешного окончания процесса формирования кортежа языка Python с аргументами функции модуля Python мы можем осуществить непосредственный вызов этой функции. Для этой цели следует в первую очередь получить ссылку на функцию из импортированного модуля Python с помощью функции PyObject_GetAttrString(), после чего воспользоваться функцией PyObject_CallObject() для ее вызова. Например:

// argv[2] хранит имя функции, реализованной в рамках модуля pModule
// pFunc является ссылкой на эту функцию
pFunc = PyObject_GetAttrString(pModule, argv[2]);
pValue = PyObject_CallObject(pFunc, pythonArgument);

Шаг 8: Обработка ошибок

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

pModule = PyImport_Import(pName);
if (pModule != NULL) {
    // Здесь следует выполнять какие-либо полезные действия
}
else {
   PyErr_Print();  // Вывод стека вызовов функций
   fprintf(stderr, "Не удалось загрузить \"%s\"\n", argv[1]);
   return 1;
}

Таким образом вы без особого труда сможете добавить различные проверки в ваши программы.

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

Шаг 9: Компиляция и исполнение

Сохраните приведенный выше код в файле с именем finalCode.c и скомпилируйте его, не забыв связать результирующий бинарный файл с библиотекой интерпретатора Python (-lpython2.7). В зависимости от используемого дистрибутива, может возникнуть необходимость в связывании бинарного файла с другой версией библиотеки интерпретатора Python (например, -lpython2.6).

$ gcc -o final finalCode.c -lpython2.7

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

./final printData printData cyberpersons

В данном примере тремя аргументами являются имя модуля Python, имя функции, реализованной в рамках этого модуля, и строка, передаваемая в качестве аргумента функции соответственно.

Вывод программы должен выглядеть аналогичным образом:

Вывод программы

Надеюсь, что данная информация была полезной для вас.