使用Python C API在C语言中调用Python代码详解
文章目录
在C++中调用Python代码的完整指南
本指南详细讲解如何在C++中通过Python C API调用Python代码,涵盖 环境配置(含PYTHONPATH/PYTHONHOME)、代码实现、跨平台编译及 关键问题解决方案,适用于Linux/Windows/macOS。
一、环境配置
1. 核心环境变量
| 变量名 | 作用 | 设置方法 |
|---|---|---|
| PYTHONHOME | 指定Python安装根目录(包含Lib和include目录) |
– 代码设置: 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
- C/C++ → 附加包含目录:
C:\Python3.10\include - 链接器 → 附加库目录:
C:\Python3.10\libs - 附加依赖项:
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
-I参数是否指向Python的include目录。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. 多线程环境崩溃
// 在子线程中调用Python前获取GIL
PyGILState_STATE gstate = PyGILState_Ensure();
// ... 执行Python代码 ...
PyGILState_Release(gstate);
5. Qt代码 <include> Python.h报错
#undef slots
#include <Python.h>
#define slots Q_SLOTS
五、最佳实践
-
版本一致性
确保Python开发环境(头文件、库文件)与运行时版本完全一致。 -
路径规范化
使用绝对路径代替相对路径,避免因工作目录变化导致的路径错误。 -
错误处理
在每次Python API调用后检查异常:if (PyErr_Occurred()) { PyErr_Print(); // 打印详细的错误堆栈 PyErr_Clear(); // 清除异常状态 } -
嵌入式Python部署
若使用嵌入式Python(如Windows嵌入式包),需将python3.x.zip解压到PYTHONHOME\Lib目录,确保标准库可用。
通过本指南,您可以在C++项目中无缝集成Python代码,适用于AI模型推理、脚本扩展等场景。
作者:晴雨日记