C++嵌入Python时如何不执行脚本直接调用Python函数
问题原因
你遇到的两次输出问题由两个原因共同导致:
- Python模块加载的固有机制:首次导入模块时,解释器会从头到尾执行模块顶层的所有代码,类、函数的定义本质也是可执行语句,必须运行后才能注册到模块命名空间供外部调用,不存在完全不运行脚本就能导入模块内函数/类的可能。你在
foo.py顶层写的foo().test()会在导入时自动执行,输出第一次foo.test。 - C++侧调用逻辑错误:
test是foo类的实例方法,并不是模块级别的独立函数,你原代码直接从模块对象取test属性本身就不符合Python的对象模型,正常情况下这里会拿到空指针触发调用失败。
正确实现方法
第一步:修改Python脚本,隔离导入时执行和直接运行时执行的逻辑
遵循Python通用编码规范,将不需要在导入时执行的业务入口代码放到if __name__ == "__main__":判断块中——当脚本被直接运行时,模块的__name__属性为"__main__",块内代码会执行;当脚本被导入时,__name__为模块名(这里是"foo"),块内代码不会触发。
修正后的foo.py代码:
# foo.py class foo: def test(self): print("foo.test") if __name__ == "__main__": # 仅直接运行脚本时执行,导入时不触发 foo().test()
第二步:修正C++侧调用逻辑
嵌入式Python调用必须先初始化解释器,按照「获取模块->获取类->实例化类->获取实例方法->调用方法」的层级调用,同时做好引用计数回收和对象合法性校验,避免内存泄漏和崩溃。
修正后的main.cpp代码:
// main.cpp #include <Python.h> int main() { // 初始化Python解释器,嵌入式调用必须先执行该步骤 Py_Initialize(); // 可选:将当前工作目录加入Python模块搜索路径,避免找不到foo.py PyRun_SimpleString("import sys; sys.path.insert(0, '.')"); PyObject* myModule = PyImport_ImportModule("foo"); if (myModule) { // 从模块中获取foo类对象 PyObject* fooCls = PyObject_GetAttrString(myModule, "foo"); if (fooCls && PyCallable_Check(fooCls)) { // 实例化foo类 PyObject* fooInstance = PyObject_CallObject(fooCls, nullptr); if (fooInstance) { // 从实例中获取test方法 PyObject* testMethod = PyObject_GetAttrString(fooInstance, "test"); if (testMethod && PyCallable_Check(testMethod)) { // 调用无参的test方法 PyObject_CallObject(testMethod, nullptr); // 释放Python对象引用,避免内存泄漏 Py_DECREF(testMethod); } Py_DECREF(fooInstance); } Py_DECREF(fooCls); } Py_DECREF(myModule); } // 关闭Python解释器,回收资源 Py_Finalize(); return 0; }
注意事项
- 不要尝试通过hack手段跳过模块顶层代码执行,否则会导致模块内的类、函数无法正常注册,后续调用必然失败。
- 所有通过Python C API获取到的
PyObject*对象,在不再使用时都需要调用Py_DECREF减少引用计数,嵌入式场景长期运行如果漏写会出现严重的内存泄漏问题。 - 调用Python对象前最好通过
PyCallable_Check校验对象是否可调用,避免空指针或类型错误导致程序崩溃。
内容的提问来源于stack exchange,提问作者happy_S
相关产品推荐
相关产品推荐

