You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在嵌入式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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:25:47