pybind11是C++与Python交互最轻量主流方案,通过PYBIND11_MODULE声明模块、m.def()绑定函数、编译为.so/.pyd实现调用;支持STL类型自动转换,类绑定需pybind11::class_。
用 pybind11 是目前 C++ 与 Python 交互最轻量、最主流的选择——它不依赖 Python C API 的底层细节,编译快,接口写法接近 Python 原生风格,且头文件即用,无需链接庞大库。
核心是用 pybind11 将 C++ 函数“绑定”为 Python 可导入的模块。关键步骤:声明模块、注册函数、编译成 .so(Linux/macOS)或 .pyd(Windows)。
#include ,并用 PYBIND11_MODULE 宏定义模块入口m.def() 暴露出去-std=c++11),并链接 pybind11 的构建系统(推荐用 cmake + find_package(pybind11))/* example.cpp */ #includeint add(int a, int b) { return a + b; }
PYBIND11_MODULE(example, m) { m.doc() = "pybind11 example plugin"; m.def("add", &add, "A function that adds two integers"); }
pybind11 自动支持常见 STL 类型的转换,但默认是值传递(拷贝),对大对象可能影响性能;若需零拷贝或原地修改,得显式控制。
std::vector、std::string、std::map 等可直接作为参数或返回值,无需额外声明std::vector 时,Python 侧拿到的是 list;传入 list 也会自动转为 std::vector
pybind11::array_t 或 pybind11::buffer,配合 NumPy 使用/* vector_example.cpp */ #include#include // 必须包含此头文件以支持 STL 容器自动转换 #include std::vector
get_range(int n) { std::vector v(n); for (int i = 0; i < n; ++i) v[i] = i; return v; } PYBIND11_MODULE(vector_example, m) { m.def("get_range", &get_range); }
多数问题出在环境路径、Python 版本不匹配或 CMake 配置遗漏,不是代码逻辑问题。
ImportError: dynamic module does not define module export function:模块名与 PYBI
ND11_MODULE 第一个参数不一致,或编译后文件名不是 xxx.cpython-*.so 形式(建议用 set_target_properties(... PROPERTIES PREFIX "") 清除前缀)undefined symbol: _Py_ZeroStruct:链接了错误的 Python 库(比如系统 Python 和 conda Python 混用),检查 cmake -DPYTHON_EXECUTABLE 是否指向你实际运行 python 的那个解释器LNK2001: unresolved external symbol PyInit_xxx:没加 __declspec(dllexport)?不用——pybind11 已通过宏处理,只需确认项目属性中“配置类型”是 Dynamic Library (.dll),且输出扩展名为 .pyd
要,而且非常值得。相比纯函数,类绑定能自然映射面向对象逻辑,支持构造、析构、成员函数、属性、重载操作符等。
pybind11::class_(m, "MyClass") 声明绑定类.def(pybind11::init()) 绑定构造函数;.def("method", &MyClass::method) 绑定成员函数.def_readwrite("name", &MyClass::name);只读用 def_readonly
pybind11::return_value_policy 控制,否则默认按值返回会触发拷贝真正容易被忽略的是 Python 解释器初始化状态——如果你在已有 Python 进程中(比如嵌入式场景)加载 pybind11 模块,确保 PyEval_InitThreads()(旧版)或 GIL 管理已就绪;而绝大多数情况(直接运行 python script.py)不需要操心这个。