在 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脚本的资料请关注代码网其它相关文章!
发表评论