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

 更新时间:2025年05月29日 09:56:11   作者:智齿受害者  
在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脚本的资料请关注脚本之家其它相关文章!

相关文章

  • QT编写简单登录界面的实现示例

    QT编写简单登录界面的实现示例

    登陆界面是网页中常见的界面,本文主要介绍了QT编写简单登录界面的实现示例,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 利用Debug调试代码解决0xC0000005: 读取位置 0x0000000000000000 时发生访问冲突问题

    利用Debug调试代码解决0xC0000005: 读取位置 0x0000000000000000 时发生访问冲突问

    这篇文章主要介绍了利用Debug调试代码解决0xC0000005: 读取位置 0x0000000000000000 时发生访问冲突,本文给大家分享完美解决方案,需要的朋友可以参考下
    2023-03-03
  • C++调用EasyX库实现嫦娥奔月小游戏

    C++调用EasyX库实现嫦娥奔月小游戏

    这篇文章主要为大家详细介绍了C++如何调用EasyX库编写一个简单的嫦娥奔月小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下
    2023-09-09
  • C++数组的定义详情

    C++数组的定义详情

    这篇文章主要介绍了C++数组的定义详情,上一篇文章我们学习了类型,接下俩我们九在类型的基础上展开本篇内容数组的常用方法以及C++标准库提供的一些关于数组的容器,需要的朋友可以参考一下,希望对你有所帮助
    2021-12-12
  • C语言实现十六进制与二进制的相互转换

    C语言实现十六进制与二进制的相互转换

    这篇文章主要为大家详细介绍了如何利用c语言实现将文件中十六进制数据与二进制数据相互转换,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的可以学习一下
    2022-11-11
  • C语言动态内存分配图文讲解

    C语言动态内存分配图文讲解

    给数组分配多大的空间?你是否和初学C时的我一样,有过这样的疑问。这一期就来聊一聊动态内存的分配,读完这篇文章,你可能对内存的分配有一个更好的理解
    2023-01-01
  • C语言 while语句的用法详解

    C语言 while语句的用法详解

    本文主要介绍C语言while 语句,这里对while 循环做详细讲解,并提供示例代码,希望能帮助学习C语言的同学
    2016-07-07
  • C语言入门篇--注释,关键字typedef及转义字符详解

    C语言入门篇--注释,关键字typedef及转义字符详解

    本篇文章是c语言基础篇,主要为大家介绍了C语言的关键字typedef,注释,转义字符的基本理论知识,希望可以帮助大家快速入门c语言的世界,更好的理解c语言
    2021-08-08
  • Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程

    Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程

    这篇文章主要介绍了Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程,对于用MAC学习Unix环境高级编程的同学会有些作用,需要的朋友可以参考下
    2014-09-09
  • C++构造函数一些常见的坑

    C++构造函数一些常见的坑

    这篇文章主要给大家分享的是C++构造函数一些常见的坑,文章围绕C++构造函数的相关资料展开关于C++构造函数坑的内容,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-01-01

最新评论