Использование интерпретатора Python внутри программы на C
Интеграция Python в приложения на C позволяет использовать преимущества скриптового языка для конфигурации, расширения функциональности или быстрого прототипирования. Основной инструмент - Python/C API, входящий в состав дистрибутива Python. Ниже рассмотрены основные подходы с примерами кода и указанием типичных проблем.
Основные методы встраивания Python в C
Базовый сценарий: инициализация интерпретатора, выполнение простого скрипта и завершение. Код на C:
#include <Python.h>
int main() {
Py_Initialize();
PyRun_SimpleString("print('Hello from Python')");
Py_Finalize();
return 0;
}Python list in c (использование списков python в c)
Компиляция: gcc -o embed embed.c -I/usr/include/python3.10 -lpython3.10. Параметры зависят от версии Python и путей установки.
Типичная ошибка:
undefined reference to Py_Initialize - отсутствует флаг -lpython3.X. Решение: проверить версию и пути (например, pkg-config --cflags --libs python3).
Как выполнить произвольный код Python и получить результат вычислений?
Использовать PyRun_String для выполнения выражения и PyObject для извлечения результата. Пример:
PyObject *pModule, *pFunc, *pArgs, *pResult;
Py_Initialize();
pModule = PyImport_ImportModule("math");
pFunc = PyObject_GetAttrString(pModule, "sqrt");
pArgs = PyTuple_Pack(1, PyFloat_FromDouble(25.0));
pResult = PyObject_CallObject(pFunc, pArgs);
double res = PyFloat_AsDouble(pResult);
printf("sqrt(25) = %f\n", res);
Py_DECREF(pResult);
Py_DECREF(pArgs);
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_Finalize();Use python in c (использование python в коде c (встраивание))
Проблема:
Утечка памяти при отсутствии Py_DECREF для каждого созданного объекта. Важно уменьшать счётчик ссылок после использования.
Как загрузить модуль Python и вызвать его функцию с передачей данных?
Импорт модуля, получение функции, формирование аргументов и вызов. Пример с передачей списка:
PyObject *pList = PyList_New(3);
for (int i = 0; i < 3; i++) {
PyList_SetItem(pList, i, PyLong_FromLong(i*10));
}
pArgs = PyTuple_Pack(1, pList);
pResult = PyObject_CallObject(pFunc, pArgs);
Python c types (библиотека ctypes в python)
После вызова обязательно освободить ссылки: Py_DECREF(pList) (если не передаёте владение) и другие.
Ошибка:
SystemError: NULL result without error in PyObject_Call - функция не найдена или модуль не импортирован. Проверить имя функции и путь к модулю.
Как обрабатывать исключения Python в коде C?
После вызова Python-функции проверить наличие ошибки с помощью PyErr_Occurred() и вывести её:
if (PyErr_Occurred()) {
PyErr_Print(); /* печатает в stderr */
PyErr_Clear();
}Python load c lib (загрузка c библиотеки в python)
Для детального анализа использовать PyErr_Fetch (получить тип, значение, traceback).
Проблема:
После обработки ошибки нужно очистить индикатор ошибки PyErr_Clear(), иначе последующие вызовы Python будут завершаться с той же ошибкой.
Как встраивать Python в многопоточное приложение?
Управление GIL с помощью пары PyGILState_Ensure / PyGILState_Release:
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
/* работа с Python API */
PyGILState_Release(gstate);
Инициализацию интерпретатора выполнять однократно до создания потоков.
Ошибка:
Вызов API без захвата GIL приводит к падению или взаимоблокировке. Всегда использовать PyGILState_Ensure в каждом потоке, где работают с Python.
Расширенные примеры встраивания
Ниже приведены более сложные сценарии, демонстрирующие взаимодействие с библиотеками и многопоточность.
/* Пример работы с numpy: создание массива из C и вызов Python-функции */
#include <numpy/arrayobject.h>
void init_numpy() {
import_array(); /* макрос для инициализации numpy */
}
int main() {
Py_Initialize();
init_numpy();
npy_intp dims[1] = {5};
PyObject *pArray = PyArray_SimpleNew(1, dims, NPY_FLOAT64);
double *data = (double*)PyArray_DATA(pArray);
for (int i = 0; i < 5; i++) data[i] = i * 1.1;
PyObject *pModule = PyImport_ImportModule("numpy");
PyObject *pFunc = PyObject_GetAttrString(pModule, "mean");
PyObject *pArgs = PyTuple_Pack(1, pArray);
PyObject *pResult = PyObject_CallObject(pFunc, pArgs);
double mean = PyFloat_AsDouble(pResult);
printf("Mean = %f\n", mean);
Py_DECREF(pResult); Py_DECREF(pArgs);
Py_DECREF(pFunc); Py_DECREF(pModule); Py_DECREF(pArray);
Py_Finalize();
return 0;
}
Mean = 2.200000
/* Встраивание в многопоточном контексте с использованием PyEval_EvalCode */
#include <pthread.h>
void* thread_func(void* arg) {
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *pGlobal = PyDict_New();
PyObject *pCode = Py_CompileString("result = thread_id * 2", "<inline>", Py_file_input);
PyObject *pLocal = PyDict_New();
PyDict_SetItemString(pLocal, "thread_id", PyLong_FromLong((long)arg));
PyEval_EvalCode(pCode, pGlobal, pLocal);
PyObject *pResult = PyDict_GetItemString(pLocal, "result");
long val = PyLong_AsLong(pResult);
printf("Thread %ld: result = %ld\n", (long)arg, val);
Py_DECREF(pLocal); Py_DECREF(pGlobal); Py_DECREF(pCode);
PyGILState_Release(gstate);
return NULL;
}
int main() {
Py_Initialize();
pthread_t threads[3];
for (long i = 0; i < 3; i++)
pthread_create(&threads[i], NULL, thread_func, (void*)i);
for (int i = 0; i < 3; i++)
pthread_join(threads[i], NULL);
Py_Finalize();
return 0;
}
Thread 0: result = 0 Thread 1: result = 2 Thread 2: result = 4
Примечание:
Для компиляции примеров требуется добавить флаги линковки numpy и pthreads (например, -lnumpy -lpthread).