目录
- 环境准备
- 简单调用示例
- python 脚本
- C 语言代码
- 多线程调用示例
- 跨平台编译
- 问题
在 C 语言中调用 Python 脚本的方式有很多,在这里使用 Python 的 C API 进行调用 Python 脚本,包括初始化解释器、执行脚本文件(导入模块和类、创建对象、调用对象方法)以及处理错误的完整流程。 参考 Python 的 C API 文档: Python/C API 参考手册
示例代码:python_c_api_demo
环境准备
- 示例使用的是 Python3.9 ,需要安装 python 3.9 以及 python3-dev
- 安装 gcc
- 执行环境必须有 Python 环境
简单调用示例
单线程导入模块以及类,创建对象,调用对象方法。
Python 脚本
example.py
import random import time class HelloWorld: def __init__(self, name): self.name = name def say_hi(self): print(f'Hi {self.name}') def add(self, a, b): return a + b def random_number(self): time.sleep(0.05) return random.randint(0, 100)
C 语言代码
直接在 example.c 中执行 Python 脚本。
#include <Python.h> int main() { // 初始化 Python 解释器 Py_Initialize(); // 添加当前目录到 sys.path(确保能导入 example.py) PyObject *pSys = PyImport_ImportModule("sys"); if (!pSys) { PyErr_Print(); fprintf(stderr, "Failed to import sys module\n"); return 1; } PyObject *pPath = PyObject_GetAttrString(pSys, "path"); if (!pPath) { PyErr_Print(); fprintf(stderr, "Failed to get sys.path\n"); Py_DECREF(pSys); return 1; } int status = PyList_Append(pPath, PyUnicode_FromString(".")); if (status == -1) { PyErr_Print(); fprintf(stderr, "Failed to append path\n"); Py_DECREF(pPath); Py_DECREF(pSys); return 1; } Py_DECREF(pPath); Py_DECREF(pSys); // 导入 example 模块 PyObject *pModule = PyImport_ImportModule("example"); if (!pModule) { PyErr_Print(); fprintf(stderr, "Failed to import example module\n"); Py_Finalize(); return 1; } // 导入 HelloWorld 类 PyObject *pClass = PyObject_GetAttrString(pModule, "HelloWorld"); if (!pClass) { PyErr_Print(); fprintf(stderr, "Failed to import HelloWorld class\n"); Py_DECREF(pModule); Py_Finalize(); return 1; } // 创建对象 char name[256] = "XiaoMing"; PyObject *pArgs = PyTuple_New(1); PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(name)); PyObject *pInstance = PyObject_CallObject(pClass, pArgs); Py_DECREF(pArgs); if (pInstance == NULL) { PyErr_Print(); fprintf(stderr, "Failed to create instance of HelloWorld\n"); Py_XDECREF(pClass); Py_XDECREF(pModule); return 1; } // 获取 say_hi 方法 PyObject *pFuncSayHi = PyObject_GetAttrString(pInstance, "say_hi"); if (!pFuncSayHi || !PyCallable_Check(pFuncSayHi)) { PyErr_Print(); fprintf(stderr, "Failed to get say_hi function\n"); Py_DECREF(pInstance); Py_DECREF(pClass); Py_DECREF(pModule); Py_Finalize(); return 1; } // 调用 say_hi 方法 PyObject_CallObject(pFuncSayHi, NULL); // 获取 add 函数 PyObject *pFuncAdd = PyObject_GetAttrString(pInstance, "add"); if (!pFuncAdd || !PyCallable_Check(pFuncAdd)) { PyErr_Print(); fprintf(stderr, "Failed to get add function\n"); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); Py_DECREF(pClass); Py_DECREF(pModule); Py_Finalize(); return 1; } // 构造参数并调用 add 函数 PyObject *pArgsAdd = PyTuple_Pack(2, PyLong_FromLong(3), PyLong_FromLong(4)); if (!pArgsAdd) { PyErr_Print(); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); Py_DECREF(pClass); Py_DECREF(pModule); Py_Finalize(); return 1; } PyObject *pResultAdd = PyObject_CallObject(pFuncAdd, pArgsAdd); if (!pResultAdd) { PyErr_Print(); fprintf(stderr, "Failed to call add function\n"); Py_DECREF(pArgsAdd); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); Py_DECREF(pClass); Py_DECREF(pModule); Py_Finalize(); return 1; } // 处理返回值 long addResult = PyLong_AsLong(pResultAdd); printf("Add Result: %ld\n", addResult); // 获取 random_number 方法 PyObject *pFuncRandomNumber = PyObject_GetAttrString(pInstance, "random_number"); if (!pFuncRandomNumber || !PyCallable_Check(pFuncRandomNumber)) { PyErr_Print(); fprintf(stderr, "Failed to get random_number function\n"); Py_DECREF(pResultAdd); Py_DECREF(pArgsAdd); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); Py_DECREF(pClass); Py_DECREF(pModule); Py_Finalize(); return 1; } // 调用 random_number 方法 PyObject *jspRandomNumber = PyObject_CallObject(pFuncRandomNumber, NULL); long randomNumber = PyLong_AsLong(pRandomNumber); printf("Random Number: %ld\n", randomNumber); // 释放资源 Py_DECREF(pRandomNumber); Py_DECREF(pFuncRandomNumber); Py_DECREF(pResultAdd); Py_DECREF(pArgsAdd); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); Py_DECREF(pClass); Py_DECREF(pModule); // 结束 Python 解释器 Py_Finalize(); return 0; }
编译命令:gcc example.c -I/usr/includephp/python3.9 -lpython3.9 -o example
多线程调用示例
当在 C 程序中创建多线程调用 Python API 时,必须注意 GIL 的获取和释放,使用 PyGILState_Ensure() 和 PyGILState_Release() 来获取和释放 GIL。 在主线程使用Py_Initialize
初始化 Python 解释器后,主线程会自动持有 GIL,必须显式释放 GIL,否则子线程中会一直阻塞在获取 GIL
example_threading.c
在主线程中初始化 Python 解释器,并导入模块和类,创建多个线程,每个线程都创建对象,并调用对象方法。
#include <Python.h> #define NUM_THREADS 5 #define NUM_ITERATIONS 50 // 线程参数结构 typedef struct { int thread_id; const char *name; PyObject *pClass; } ThreadArgs; // 生成随机字符串的函数 void generate_random_string(char *buffer, int length, const char *charset) { int charset_len = strlen(charset); for (int i = 0; i < length; ++i) { int index = rand() % charset_len; // 从字符集中随机选一个字符 buffer[i] = charset[index]; } } // 线程函数 void *thread_func(void *args) { ThreadArgs *targs = (ThreadArgs *)args; int tid = targs->thread_id; printf("Thread %d started\n", tid); PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyObject *pArgs = PyTuple_New(1); PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(targs->name)); PyGILState_Release(gstate); gstate = PyGILState_Ensure(); PyObject *pInstance = PyObject_CallObject(targs->pClass, pArgs); Py_DECREF(pArgs); if (pInstance == NULL) { PyErr_Print(); fprintf(stderr, "Failed to create instance of HelloWorld\n"); PyGILSta编程客栈te_Release(gstate); return NULL; } PyGILState_Release(gstate); // 获取 say_hi 方法 gstate = PyGILState_Ensure(); PyObject *pFuncSayHi = PyObject_GetAttrString(pInstance, "say_hi"); if (!pFuncSayHi || !PyCallable_Check(pFuncSayHi)) { PyErr_Print(); fprintf(stderr, "Failed to get say_hi function\n"); Py_DECREF(pInstance); PyGILState_Release(gstate); Py_Finalize(); return NULL; } // 调用 say_hi 方法 PyObject_CallObject(pFuncSayHi, NULL); PyGILState_Release(gstate); // 获取 add 函数 gstate = PyGILState_Ensure(); PyObject *pFuncAdd = PyObject_GetAttrString(pInstance, "add"); if (!pFuncAdd || !PyCallable_Check(pFuncAdd)) { PyErr_Print(); fprintf(stderr, "Failed to get add function\n"); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); PyGILState_Release(gstate); Py_Finalize(); return NULL; } // 构造参数并调用 add 函数 PyObject *pArgsAdd = PyTuple_Pack(2, PyLong_FromLong(3), PyLong_FromLong(4)); if (!pArgsAdd) { PyErr_Print(); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); PyGILState_Release(gstate); Py_Finalize(); return NULL; } PyObject *pResultAdd = PyObject_CallObject(pFuncAdd, pArgsAdd); if (!pResultAdd) { PyErr_Print(); fprintf(stderr, "Failed to call add function\n"); Py_DECREF(pArgsAdd); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); PyGILState_Release(gstate); Py_Finalize(); return NULL; } // 处理返回值 long addResult = PyLong_AsLong(pResultAdd); PyGILState_Release(gstate); printf("Add Result: %ld\n", addResult); // 获取 random_number 方法 gstate = PyGILState_Ensure(); PyObject *pFuncRandomNumber = PyObject_GetAttrString(pInstance, "random_number"); if (!pFuncRandomNumber || !PyCallable_Check(pFuncRandomNumber)) { PyErr_Print(); fprintf(stderr, "Failed to get random_number function\n"); Py_DECREF(pResultAdd); Py_DECREF(pArgsAdd); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); PyGILState_Release(gstate); Py_Finalize(); return NULL; } PyGILState_Release(gstate); srand(time(NULL) + tid); // 循环多次调用 random_number 方法 for (int i = 0; i < NUM_ITERATIONS; i++) { gstate = PyGILState_Ensure(); PyObject *pRandomNumber = PyObject_CallObject(pFuncRandomNumber, NULL); long randomNumber = PyLong_AsLong(pRandomNumber); printf("Thread %d, Iteration %d, Random Number: %ld\n", tid, i, randomNumber); Py_DECREF(pRandomNumber); PyGILState_Release(gstate); usleep(10000); } // 编程释放资源 gstate = PyGILState_Ensure(); Py_DECREF(pFuncRandomNumber); Py_DECREF(pResultAdd); Py_DECREF(pArgsAdd); Py_DECREF(pFuncAdd); Py_DECREF(pFuncSayHi); Py_DECREF(pInstance); PyGILState_Release(gstate); printf("Thread %d end\n", tid); return NULL; } int main() { // 主线程初始化一次 Python 解释器 Py_Initialize(); // 添加当前目录到 sys.path(确保能导入 example.py) PyObject *pSys = PyImport_ImportModule("sys"); if (!pSys) { PyErr_Print(); fprintf(stderr, "Failed to import sys module\n"); return 1; } PyObject *pPath = PyObject_GetAttrString(pSys, "path"); if (!pPath) { PyErr_Print(); fprintf(stderr, "Failed to get sys.path\n"); Py_DECREF(pSys); return 1; } int status = PyList_Append(pPath, PyUnicode_FromString(".")); if (status == -1) { PyErr_Print(); fprintf(stderr, "Failed to append path\n"); Py_DECREF(pPath); Py_DECREF(pSys); return 1; } Py_DECREF(pPath); Py_DECREF(pSys); // 导入 example 模块 PyObject *pModule = PyImport_ImportModule("example"); if (!pModule) { PyErr_Print(); fprintf(stderr, "Failed to import example module\n"); Py_Finalize(); return 1; } // 导入 HelloWorld 类 PyObject *pClass = PyObject_GetAttrString(pModule, "HelloWorld"); if (!pClass) { PyErr_Print(); fprintf(stderr, "Failed to import HelloWorld class\n"); Py_DECREF(pModule); Py_Finalize(); return 1; } PyEval_SaveThread(); // 释放 GIL srand(time(NULL)); // 初始化随机种子 const char *charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 创建线程 pthread_t threads[NUM_THREADS]; ThreadArgs thread_args[NUM_THREADS]; for (int i = 0; i < NUM_THREADS; ++i) { printf("Create thread %d\n", i); char buffer[5]; // 长度为 5 个结束符 generate_random_string(buffer, 5, charset); thread_args[i].thread_id = i; thread_args[i].name = buffer; thread_args[i].pClass = pClass; int rc = pthread_create(&threads[i], NULL, thread_func, &thread_args[i]); if (rc) { fprintf(stderr, "Error creating thread %d\n", i); return EXIT_FAILURE; } // usleep(100000); } printf("create threads success\n"); for (int i = 0; i < NUM_THREADS; ++i) { pthread_join(threads[i], NULL); } PyGILState_STATE gstate = PyGILState_Ensure(); Py_DECREF(pClass); Py_DECREF(pModule); // 结束 Python 解释器 Py_Finalize(); return 0; }
编译命令:gcc example_threading.c -I/usr/include/pythhrBNhPPGon3.9 -lpython3.9 -lpthread -o example_threading
跨平台编译
在 x64 上编译到 arm64 平台的可执行文件,需要将目标设备上的 python 相关头文件和库文件复制到编译机上,并使用 aarch64-linux-gnu-gcc 编译。
编译命令:aarch64-linux-gnu-gcc example_threading.c -o example_threading_arm64 -I./arm64-python3.12/include/python3.12 -L./arm64-python3.12/lib -lpython3.12 -lpthread -lm -lutil -ldl -Wl,-rpath,.
问题
在 Python 脚本中使用 Numpy 时,出现报错:numpy:DLL load failed while importing _multiarray_umath:
,如果 Python 库中使用了 C 扩展,应该都会有这个问题,解决方案:在初始化 python 解释器前,动态加载 python 的 so 库
#include <dlfcn.h> dlopen("libpython3.9.so", RTLD_LAZY | RTLD_GLOBAL)
以上就是C语言中调用Python脚本的方法详解的详细内容,更多关于C语言调用Python脚本的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论