当前位置: 代码网 > it编程>前端脚本>Python > C语言中调用Python脚本的方法详解

C语言中调用Python脚本的方法详解

2025年05月29日 Python 我要评论
在 c 语言中调用 python 脚本的方式有很多,在这里使用 python 的 c api 进行调用 python 脚本,包括初始化解释器、执行脚本文件(导入模块和类、创建对象、调用对象方法)以及处

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com