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脚本的资料请关注脚本之家其它相关文章!

相关文章

  • C++中new与delete、malloc与free应用分析

    C++中new与delete、malloc与free应用分析

    这篇文章主要介绍了C++中new与delete、malloc与free应用分析,很重要的概念,需要的朋友可以参考下
    2014-08-08
  • C++中的string库函数常见函数的作用和使用方法

    C++中的string库函数常见函数的作用和使用方法

    这篇文章主要介绍了C++中的string库函数常见函数的作用和使用方法,库函数的灵活应用是程序员的一大重要技能,本文通过实例实例代码给大家讲解的非常详细,需要的朋友可以参考下
    2022-04-04
  • C++索引越界的解决方法

    C++索引越界的解决方法

    本文主要介绍了C++索引越界的解决方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • QT实现将两个时间相加的算法[hh: mm + hh: mm]的示例代码

    QT实现将两个时间相加的算法[hh: mm + hh: mm]的示例代码

    本文主要介绍了QT实现将两个时间相加的算法[hh: mm + hh: mm]的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Qt控件之QDialog使用及技巧小结

    Qt控件之QDialog使用及技巧小结

    QDialog是Qt中的一个类,继承自QWidget类,用于创建对话框窗口,可以显示模态或非模态的对话框,本文就来介绍一下Qt控件之QDialog使用及技巧小结,感兴趣的可以了解一下
    2023-10-10
  • C++编程实现获取当前可执行文件名称

    C++编程实现获取当前可执行文件名称

    在C++开发中,有时需要获取当前可执行文件的名称用来处理一些事务,本文总结了在Windows和Linux平台上获取当前可执行文件名称的多种方法,大家可以根据需要进行选择
    2026-03-03
  • C语言实现简单电子通讯录(2)

    C语言实现简单电子通讯录(2)

    这篇文章主要为大家详细介绍了C语言实现简单电子通讯录的第二部分,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • Qt中树形控件Tree Widget的使用方法汇总

    Qt中树形控件Tree Widget的使用方法汇总

    最近小编在研究Tree Widget树形控件的相关知识,这种控件其实有时还是很有用处的,我主要利用的是带有复选框的树形控件,下面通过实例代码给大家介绍下Qt中树形控件Tree Widget的一些使用方法,感兴趣的朋友一起学习吧
    2021-11-11
  • C++实现正整数的四则运算表达式

    C++实现正整数的四则运算表达式

    这篇文章主要为大家详细介绍了C++实现正整数的四则运算表达式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • C++实现LeetCode(148.链表排序)

    C++实现LeetCode(148.链表排序)

    这篇文章主要介绍了C++实现LeetCode(148.链表排序),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论