使用Python C API在C语言中调用Python代码详解

文章目录

  • 在C++中调用Python代码的完整指南
  • 一、环境配置
  • 1. 核心环境变量
  • 2. 跨平台配置
  • **Linux/macOS**
  • **Windows**
  • 二、代码实现
  • 1. 完整代码示例
  • 2. 关键代码解释
  • 三、核心注意事项
  • 1. 引用计数管理
  • 2. 线程安全
  • 3. 数据类型转换
  • 四、常见问题与解决方案
  • 1. **编译错误:`Python.h not found`**
  • 2. **运行时错误:`Fatal Python error: initfsencoding`**
  • 3. **模块导入失败:`ModuleNotFoundError`**
  • 4. **多线程环境崩溃**
  • 5. **Qt代码 \<include\> Python.h报错**
  • 五、最佳实践
  • 在C++中调用Python代码的完整指南

    本指南详细讲解如何在C++中通过Python C API调用Python代码,涵盖 环境配置(含PYTHONPATH/PYTHONHOME)代码实现跨平台编译关键问题解决方案,适用于Linux/Windows/macOS。


    一、环境配置

    1. 核心环境变量
    变量名 作用 设置方法
    PYTHONHOME 指定Python安装根目录(包含Libinclude目录) 代码设置: Py_SetPythonHome(L"/path/to/python")
    系统变量: 直接配置环境变量
    PYTHONPATH 添加自定义模块搜索路径(优先级高于sys.path 代码设置: PyRun_SimpleString("sys.path.append('/path')")
    系统变量: 配置环境变量
    2. 跨平台配置
    Linux/macOS
  • 编译命令:
    # 指定头文件和库路径(假设Python安装路径为/opt/python3.10)
    g++ main.cpp -I/opt/python3.10/include/python3.10 -L/opt/python3.10/lib -lpython3.10
    
  • 运行时路径:
    export LD_LIBRARY_PATH=/opt/python3.10/lib:$LD_LIBRARY_PATH
    
  • Windows
  • Visual Studio配置:
    1. C/C++ → 附加包含目录: C:\Python3.10\include
    2. 链接器 → 附加库目录: C:\Python3.10\libs
    3. 附加依赖项: python310.lib(Release)或python310_d.lib(Debug)

  • 二、代码实现

    1. 完整代码示例
    #include <Python.h> //Qt开发时包含Python.h头文件需要特殊处理,见下述常见问题与解决方案章节
    #include <iostream>
    
    int main() {
        // 1. 设置PYTHONHOME(可选,需在初始化前调用)
        Py_SetPythonHome(L"C:\\Python310");  // Windows示例路径
        
        // 2. 初始化Python解释器
        Py_Initialize();
        if (!Py_IsInitialized()) {
            std::cerr << "Python初始化失败!" << std::endl;
            return -1;
        }
    
        // 3. 动态添加模块搜索路径
        PyRun_SimpleString("import sys");
        PyRun_SimpleString("sys.path.append('./custom_modules')");  // 添加自定义路径
        
        // 4. 调用Python函数
        PyObject *pModule = nullptr, *pFunc = nullptr, *pArgs = nullptr, *pResult = nullptr;
        
        // 导入模块
        pModule = PyImport_ImportModule("math");
        if (!pModule) {
            PyErr_Print();
            goto cleanup;
        }
        
        // 获取函数
        pFunc = PyObject_GetAttrString(pModule, "sqrt");
        if (!pFunc || !PyCallable_Check(pFunc)) {
            PyErr_Print();
            goto cleanup;
        }
        
        // 构造参数(以sqrt(9)为例)
        pArgs = PyTuple_Pack(1, PyFloat_FromDouble(9.0));
        
        // 执行函数
        pResult = PyObject_CallObject(pFunc, pArgs);
        if (pResult) {
            double result = PyFloat_AsDouble(pResult);
            std::cout << "sqrt(9) = " << result << std::endl;  // 输出3.0
            Py_DECREF(pResult);
        }
    
    cleanup:
        // 5. 清理资源(安全释放)
        Py_XDECREF(pArgs);    // 可处理NULL指针
        Py_XDECREF(pFunc);
        Py_XDECREF(pModule);
        
        // 6. 关闭解释器
        Py_Finalize();
        return 0;
    }
    
    2. 关键代码解释
  • Py_SetPythonHome: 显式设置Python安装路径,解决嵌入式环境或非标准安装路径问题。
  • PyRun_SimpleString: 动态修改sys.path,无需依赖系统环境变量。
  • Py_XDECREF: 安全释放对象,避免因NULL指针导致崩溃。

  • 三、核心注意事项

    1. 引用计数管理
    API函数 作用 使用场景
    Py_DECREF(obj) 减少对象引用计数(若计数为0则释放内存) 明确知道obj非NULL时使用
    Py_XDECREF(obj) 同上,但允许obj为NULL 不确定obj是否为空时使用

    规则: 每个PyObject*在不再使用时必须调用DECREF,否则导致内存泄漏。

    2. 线程安全

    若在C++多线程中调用Python代码,需获取全局解释器锁(GIL):

    PyGILState_STATE gstate = PyGILState_Ensure();  // 获取GIL
    // 执行Python代码(如调用函数、操作对象)
    PyGILState_Release(gstate);                    // 释放GIL
    
    3. 数据类型转换
    C++类型 Python API创建函数 Python类型到C++提取函数
    int PyLong_FromLong(42) PyLong_AsLong(py_obj)
    double PyFloat_FromDouble(3.14) PyFloat_AsDouble(py_obj)
    std::string PyUnicode_FromString("hello") PyUnicode_AsUTF8(py_obj)

    四、常见问题与解决方案

    1. 编译错误:Python.h not found
  • 原因: 头文件路径未正确配置。
  • 解决:
  • Linux/macOS: 检查-I参数是否指向Python的include目录。
  • Windows: 确认Visual Studio的“附加包含目录”设置。
  • 2. 运行时错误:Fatal Python error: initfsencoding
  • 原因: PYTHONHOME未正确指向Python安装根目录。
  • 解决:
    设置环境变量PYTHONHOME指向Python安装根目录
    或在代码中显式设置
    // 在代码中显式设置路径(示例为Windows路径)
    Py_SetPythonHome(L"C:\\Python310");
    
  • 3. 模块导入失败:ModuleNotFoundError
  • 原因: 模块路径未添加到sys.path
  • 解决:
    环境变量PYTHONPATH追加模块路径
  • export PYTHONPATH="/your/custom/path:$PYTHONPATH"
    

    或在代码中显式设置

    // 动态添加路径
    PyRun_SimpleString("sys.path.append('/path/to/module')");
    
    4. 多线程环境崩溃
  • 原因: 未正确管理GIL。
  • 解决:
    // 在子线程中调用Python前获取GIL
    PyGILState_STATE gstate = PyGILState_Ensure();
    // ... 执行Python代码 ...
    PyGILState_Release(gstate);
    
  • 5. Qt代码 <include> Python.h报错
  • 原因: Qt slots定义与Python内容冲突。
  • 解决:
    #undef slots
    #include <Python.h>
    #define slots Q_SLOTS
    

  • 五、最佳实践

    1. 版本一致性
      确保Python开发环境(头文件、库文件)与运行时版本完全一致。

    2. 路径规范化
      使用绝对路径代替相对路径,避免因工作目录变化导致的路径错误。

    3. 错误处理
      在每次Python API调用后检查异常:

      if (PyErr_Occurred()) {
          PyErr_Print();  // 打印详细的错误堆栈
          PyErr_Clear();  // 清除异常状态
      }
      
    4. 嵌入式Python部署
      若使用嵌入式Python(如Windows嵌入式包),需将python3.x.zip解压到PYTHONHOME\Lib目录,确保标准库可用。


    通过本指南,您可以在C++项目中无缝集成Python代码,适用于AI模型推理、脚本扩展等场景。

    作者:晴雨日记

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用Python C API在C语言中调用Python代码详解

    发表回复