C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C语言调用Python脚本

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

作者:智齿受害者

在C语言中调用 Python 脚本的方式有很多,这篇文章主要为大家介绍了人家使用 Python 的 C API 进行调用 Python 脚本,需要的可以参考一下

在 C 语言中调用 Python 脚本的方式有很多,在这里使用 Python 的 C API 进行调用 Python 脚本,包括初始化解释器、执行脚本文件(导入模块和类、创建对象、调用对象方法)以及处理错误的完整流程。 参考 Python 的 C API 文档: Python/C API 参考手册

示例代码:python_c_api_demo

环境准备

简单调用示例

单线程导入模块以及类,创建对象,调用对象方法。

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

您可能感兴趣的文章:
阅读全文