如何在嵌入式Python解释器中向Python函数传递C++对象
解决嵌入式Python中传递C++对象给Python函数的问题
我之前处理过几乎一模一样的场景——在嵌入式Python里传递SWIG包装的C对象给Python函数,核心问题出在**你直接传递了原生C指针/对象,而SWIG生成的Python模块只能识别它自己包装的代理对象**,再加上如果没正确初始化SWIG模块,Python端根本不知道MyClass这个类型存在。下面分两种方案给你解决:
方案一:基于SWIG的正确实现
关键前提
首先确保你的SWIG接口文件passmetopython.i是正确的,比如:
%module passmetopython %{ #include "MyClass.h" // 引入你的C++类头文件 %} // 导出MyClass的所有内容(或者按需导出方法) %include "MyClass.h"
编译这个接口文件生成passmetopython_wrap.cxx和passmetopython_wrap.h,这些文件是SWIG连接C++和Python的核心。
C++端嵌入式代码修改
你需要做两个核心操作:初始化SWIG模块 + 用SWIG工具函数包装C++对象,代码示例如下:
#include <Python.h> #include "MyClass.h" #include "passmetopython_wrap.h" // 引入SWIG生成的包装头文件 int main() { // 1. 初始化Python解释器 Py_Initialize(); // 2. 必须初始化SWIG生成的模块(核心!否则Python识别不了MyClass类型) // Python 2用initpassmetopython(); Python 3用PyInit_passmetopython(); initpassmetopython(); // 3. 创建原生C++对象 MyClass* my_cpp_obj = new MyClass(); // 4. 用SWIG的工具函数把C++对象转换成Python代理对象 PyObject* py_obj = SWIG_NewPointerObj( (void*)my_cpp_obj, SWIGTYPE_p_MyClass, // SWIG生成的类型描述符,在wrap.h里定义 SWIG_POINTER_DISOWN // 表示C++端保留对象所有权,需要手动delete;如果要让Python管理,用SWIG_POINTER_OWN ); // 5. 加载Python脚本并获取execute函数 PyObject* pModule = PyImport_ImportModule("你的脚本文件名(不带.py)"); if (!pModule) { PyErr_Print(); return 1; } PyObject* pExecuteFunc = PyObject_GetAttrString(pModule, "execute"); if (!pExecuteFunc || !PyCallable_Check(pExecuteFunc)) { PyErr_Print(); return 1; } // 6. 调用Python函数,传递包装后的对象 PyObject* pArgs = PyTuple_New(1); PyTuple_SetItem(pArgs, 0, py_obj); // PyTuple会接管py_obj的引用,不用手动减引用 PyObject* pResult = PyObject_CallObject(pExecuteFunc, pArgs); if (!pResult) { PyErr_Print(); } else { // 处理返回结果(如果有) Py_DECREF(pResult); } // 7. 资源清理 Py_DECREF(pArgs); Py_DECREF(pExecuteFunc); Py_DECREF(pModule); Py_Finalize(); // 如果用了SWIG_POINTER_DISOWN,手动释放C++对象 delete my_cpp_obj; return 0; }
为什么之前失败?
- 你没有调用
initpassmetopython(),SWIG的类型转换器没注册到Python解释器里,Python端不知道MyClass对应的类型。 - 直接传递
MyClass*给Python,Python无法识别这个原生指针,必须通过SWIG_NewPointerObj生成对应的代理对象。
方案二:用Cython简化实现(推荐,更直观)
如果你觉得SWIG的类型转换太繁琐,Cython可以更简洁地实现C++和Python的交互,而且嵌入式代码更易维护。
步骤1:编写Cython包装文件passmetopython.pyx
# 声明C++类的接口 cdef extern from "MyClass.h": cdef cppclass MyClass: MyClass() // 这里列出你的MyClass的方法,比如: void do_something() # 定义Python可访问的包装类 cdef class PyMyClass: cdef MyClass* ptr # 保存原生C++对象指针 def __cinit__(self): self.ptr = new MyClass() def __dealloc__(self): del self.ptr # 对象销毁时释放C++内存 # 暴露方法给Python def do_something(self): self.ptr.do_something() # 暴露一个API函数,让C++端可以把原生指针转换成Python对象 cdef api PyObject* wrap_myclass(MyClass* cpp_ptr): cdef PyMyClass py_obj = PyMyClass.__new__(PyMyClass) py_obj.ptr = cpp_ptr // 这里如果C++端要保留所有权,注释掉__dealloc__里的del,或者加参数控制 return <PyObject*>py_obj
步骤2:编译Cython模块
编写setup.py:
from setuptools import setup, Extension from Cython.Build import cythonize ext_modules = [ Extension( "passmetopython", ["passmetopython.pyx"], language="c++", include_dirs=["."], # 替换成你的MyClass.h所在目录 ) ] setup( name="passmetopython", ext_modules=cythonize(ext_modules), )
运行python setup.py build_ext --inplace生成编译后的模块。
步骤3:C++端嵌入式代码
#include <Python.h> #include "MyClass.h" #include "passmetopython_api.h" // Cython生成的API头文件 int main() { Py_Initialize(); // 导入Cython模块 PyObject* pModule = PyImport_ImportModule("passmetopython"); if (!pModule) { PyErr_Print(); return 1; } // 初始化Cython API import_passmetopython(); // 创建C++对象 MyClass* my_cpp_obj = new MyClass(); // 用Cython的API函数包装成Python对象 PyObject* py_obj = wrap_myclass(my_cpp_obj); // 获取并调用execute函数(和SWIG方案一样) PyObject* pExecuteFunc = PyObject_GetAttrString(pModule, "execute"); PyObject* pArgs = PyTuple_New(1); PyTuple_SetItem(pArgs, 0, py_obj); PyObject* pResult = PyObject_CallObject(pExecuteFunc, pArgs); if (!pResult) { PyErr_Print(); } else { Py_DECREF(pResult); } // 资源清理 Py_DECREF(pArgs); Py_DECREF(pExecuteFunc); Py_DECREF(pModule); Py_Finalize(); // 如果C++端保留所有权,手动释放 delete my_cpp_obj; return 0; }
常见注意事项
- 内存所有权:一定要明确谁负责释放C对象,避免内存泄漏或双重释放。比如用SWIG的
SWIG_POINTER_OWN时,Python会在代理对象被垃圾回收时自动释放C对象;用SWIG_POINTER_DISOWN则需要C++端手动释放。 - Python版本兼容性:Python 2和3的模块初始化函数名不同,SWIG生成的函数名也会有差异,注意对应。
内容的提问来源于stack exchange,提问作者Totte Karlsson
相关产品推荐
相关产品推荐

