开发者

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

开发者 https://www.devze.com 2025-05-29 10:41 出处:网络 作者: 智齿受害者
目录环境准备简单调用示例python 脚本C 语言代码多线程调用示例跨平台编译问题在 C 语言中调用 Python 脚本的方式有很多,在这里使用 Python 的 C API 进行调用 Python 脚本,包括初始化解释器、执行脚本文件(导入模
目录
  • 环境准备
  • 简单调用示例
    • python 脚本
    • C 语言代码
  • 多线程调用示例
    • 跨平台编译
      • 问题

        在 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 *jspRandomNumber = 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/includephp/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");
                PyGILSta编程客栈te_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/pythhrBNhPPGon3.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脚本的资料请关注编程客栈(www.devze.com)其它相关文章!

        0

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        关注公众号