在 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 *prandomnumber = 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/include/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"); pygilstate_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/python3.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脚本的资料请关注代码网其它相关文章!
发表评论