C++嵌入Python调用hello_world.py中say_hello函数时pModule为nullptr的问题解决求助
我来帮你排查这个问题——PyImport_Import返回nullptr的核心原因几乎都是Python解释器找不到你的hello_world模块,结合你的目录结构,我们一步步解决:
第一步:先添加错误诊断代码,明确具体问题
你的C++代码目前没有错误检查逻辑,这很难定位具体问题。在调用PyImport_Import后立即加上错误打印,能直接看到Python抛出的异常信息:
pName = PyUnicode_FromString("hello_world"); pModule = PyImport_Import(pName); if (pModule == nullptr) { PyErr_Print(); // 打印Python的错误信息,这会告诉你到底为什么找不到模块 Py_DECREF(pName); Py_Finalize(); return 1; }
运行后你大概率会看到类似ModuleNotFoundError: No module named 'hello_world'的错误,这就坐实了路径问题。
第二步:把hello_world.py所在目录加入Python的sys.path
Python只会在sys.path列表里的目录查找模块,而你的程序运行时的工作目录大概率是项目根目录(不是src目录),所以需要手动把src目录添加进去。在Py_Initialize之后、导入模块之前添加这段代码:
Py_Initialize(); Py_SetProgramName((wchar_t*)argv[0]); // 新增:添加src目录到sys.path PyObject* sysPath = PySys_GetObject((char*)"path"); const char* srcDir = "./src"; // 相对于项目根目录的路径,若运行目录不同需调整 PyList_Append(sysPath, PyUnicode_FromString(srcDir));
如果你的可执行文件生成在构建子目录(比如build/bin),更稳妥的方式是通过argv[0]动态获取路径:
#include <filesystem> namespace fs = std::filesystem; // ... 在Py_Initialize之后 fs::path exePath = fs::absolute(argv[0]); fs::path srcDir = exePath.parent_path() / "../src"; // 适配build/bin到src的相对路径 PyList_Append(sysPath, PyUnicode_FromString(srcDir.c_str()));
第三步:完善C++代码的内存管理和错误检查
你的代码还缺少对PyObject的引用计数管理,容易引发内存泄漏。修改后的完整代码如下:
#include <python3.10/Python.h> #include <exception> #include <filesystem> #include <cstdio> namespace fs = std::filesystem; int main(int argc, char* argv[]) { if (!Py_Initialize()) { fprintf(stderr, "Failed to initialize Python interpreter\n"); return 1; } Py_SetProgramName((wchar_t*)argv[0]); // 添加src目录到sys.path PyObject* sysPath = PySys_GetObject((char*)"path"); if (!sysPath) { PyErr_Print(); Py_Finalize(); return 1; } fs::path exePath = fs::absolute(argv[0]); fs::path srcDir = exePath.parent_path() / "../src"; PyObject* srcPathObj = PyUnicode_FromString(srcDir.c_str()); if (!srcPathObj || PyList_Append(sysPath, srcPathObj) == -1) { PyErr_Print(); Py_XDECREF(srcPathObj); Py_Finalize(); return 1; } Py_DECREF(srcPathObj); PyObject *pModule, *pName, *pDict, *pFunc; pName = PyUnicode_FromString("hello_world"); if (!pName) { PyErr_Print(); Py_Finalize(); return 1; } pModule = PyImport_Import(pName); if (!pModule) { PyErr_Print(); Py_DECREF(pName); Py_Finalize(); return 1; } Py_DECREF(pName); pDict = PyModule_GetDict(pModule); if (!pDict) { PyErr_Print(); Py_DECREF(pModule); Py_Finalize(); return 1; } pFunc = PyDict_GetItemString(pDict, "say_hello"); if (!pFunc || !PyCallable_Check(pFunc)) { PyErr_Print(); Py_DECREF(pModule); Py_Finalize(); return 1; } PyObject* result = PyObject_CallObject(pFunc, NULL); if (!result) { PyErr_Print(); } Py_XDECREF(result); Py_DECREF(pModule); Py_Finalize(); return 0; }
第四步:优化CMake配置细节
你的CMakeLists存在重复调用find_package(Python)的问题,根目录已经配置过的话,src目录无需重复调用。另外可以指定可执行文件输出路径,让路径拼接更稳定:
在根目录的CMakeLists.txt中添加:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
这样可执行文件会生成在build/bin目录下,和代码里的../src路径逻辑完全匹配。
最后,确保hello_world.py确实在src目录下,从项目根目录运行可执行文件即可正常调用Python函数。
内容的提问来源于stack exchange,提问作者Dark_Phoenix

