从C++导入Python模块遇ModuleNotFoundError及Py_SetPath用法问题
解决C++嵌入Python时的模块导入与路径配置问题
问题1:ModuleNotFoundError: No module named '.' 根源
你直接给PyImport_ImportModule传了../test/test.py这个文件路径,但这个函数的设计是接受模块名(比如test),不是带路径和后缀的文件名。Python会把../test/test.py解析成包含.的无效模块名,直接触发找不到模块的错误。
问题2:ModuleNotFoundError: No module named 'encodings' 根源
Py_SetPath会完全覆盖Python默认的sys.path,如果你只设置了自己的项目路径,Python就找不到自带的标准库(比如encodings),从而启动崩溃。正确做法是在原有sys.path基础上添加自己的路径,而非直接替换。
修正后的完整代码
添加了错误打印调试、正确的路径处理和sys.path配置:
#include <Python.h> #include <filesystem> #include <iostream> #include <string> namespace fs = std::filesystem; // 打印Python详细错误栈,调试必备 void print_python_error() { PyErr_Print(); } int python_exec(const fs::path& module_dir, const std::string& module_name, std::string_view function) { // 将模块所在目录添加到Python的搜索路径 PyObject* sys_path = PySys_GetObject("path"); PyObject* dir_path = PyUnicode_FromString(module_dir.string().c_str()); if (!dir_path || PyList_Append(sys_path, dir_path) == -1) { print_python_error(); Py_XDECREF(dir_path); return -1; } Py_DECREF(dir_path); // 导入目标模块 PyObject* module = PyImport_ImportModule(module_name.c_str()); if (!module) { print_python_error(); return -1; } // 获取目标函数 PyObject* symbol = PyObject_GetAttrString(module, function.data()); if (!symbol) { print_python_error(); Py_DECREF(module); return -1; } // 调用函数并返回结果 int result = -1; if (PyCallable_Check(symbol)) { PyObject* value = PyObject_CallObject(symbol, nullptr); if (value) { result = PyLong_AsLong(value); Py_DECREF(value); } else { print_python_error(); } } else { std::cerr << "指定符号不是可调用函数" << std::endl; } // 清理Python对象,避免内存泄漏 Py_DECREF(symbol); Py_DECREF(module); return result; } int main(int argc, char** argv) { if (argc < 2) { std::cerr << "用法: " << argv[0] << " <模块文件路径>" << std::endl; return 1; } const fs::path module_path = argv[1]; const fs::path module_dir = module_path.parent_path(); // 提取模块所在目录(比如../test) const std::string module_name = module_path.stem().string(); // 提取模块名(比如test) // 初始化Python环境 Py_Initialize(); // 执行Python函数并输出结果 std::cout << python_exec(module_dir, module_name, "my_func") << std::endl; // 清理Python环境 Py_Finalize(); return 0; }
关键注意事项
- 模块导入规则:
PyImport_ImportModule只接受模块名,不能传文件路径。需先把模块所在目录加入sys.path,再导入模块名。 - sys.path配置:不要用
Py_SetPath直接覆盖默认路径,而是通过PySys_GetObject("path")获取原有路径列表,再添加自己的目录,避免破坏Python标准库的查找路径。 - 错误调试:务必调用
PyErr_Print()打印Python的错误栈,比单纯返回-1更容易定位问题。 - 跨平台路径处理:用C++17的
filesystem库处理路径,自动适配Windows和Linux的路径分隔符。
内容的提问来源于stack exchange,提问作者user22124714
相关产品推荐
相关产品推荐

