c++代码如何与Python进行交互? (pybind11基础教程)

pybind11是C++与Python交互最轻量主流方案,通过PYBIND11_MODULE声明模块、m.def()绑定函数、编译为.so/.pyd实现调用;支持STL类型自动转换,类绑定需pybind11::class_。

pybind11 是目前 C++ 与 Python 交互最轻量、最主流的选择——它不依赖 Python C API 的底层细节,编译快,接口写法接近 Python 原生风格,且头文件即用,无需链接庞大库。

怎么让 Python 调用 C++ 函数?

核心是用 pybind11 将 C++ 函数“绑定”为 Python 可导入的模块。关键步骤:声明模块、注册函数、编译成 .so(Linux/macOS)或 .pyd(Windows)。

  • 必须包含 #include ,并用 PYBIND11_MODULE 宏定义模块入口
  • C++ 函数本身无需修改,直接在绑定代码里用 m.def() 暴露出去
  • 编译需启用 C++11 或更高标准(-std=c++11),并链接 pybind11 的构建系统(推荐用 cmake + find_package(pybind11)
/* example.cpp */
#include 

int 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"); }

如何传递和返回复杂类型(比如 std::vector)?

pybind11 自动支持常见 STL 类型的转换,但默认是值传递(拷贝),对大对象可能影响性能;若需零拷贝或原地修改,得显式控制。

  • std::vectorstd::stringstd::map 等可直接作为参数或返回值,无需额外声明
  • 返回 std::vector 时,Python 侧拿到的是 list;传入 list 也会自动转为 std::vector
  • 若想避免拷贝(例如只读访问大数组),可用 pybind11::array_tpybind11::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:模块名与 PYBIND11_MODULE 第一个参数不一致,或编译后文件名不是 xxx.cpython-*.so 形式(建议用 set_target_properties(... PROPERTIES PREFIX "") 清除前缀)
  • undefined symbol: _Py_ZeroStruct:链接了错误的 Python 库(比如系统 Python 和 conda Python 混用),检查 cmake -DPYTHON_EXECUTABLE 是否指向你实际运行 python 的那个解释器
  • Windows 下报 LNK2001: unresolved external symbol PyInit_xxx:没加 __declspec(dllexport)?不用——pybind11 已通过宏处理,只需确认项目属性中“配置类型”是 Dynamic Library (.dll),且输出扩展名为 .pyd

要不要用 pybind11::class_ 绑定 C++ 类?

要,而且非常值得。相比纯函数,类绑定能自然映射面向对象逻辑,支持构造、析构、成员函数、属性、重载操作符等。

  • pybind11::class_(m, "MyClass") 声明绑定类
  • .def(pybind11::init()) 绑定构造函数;.def("method", &MyClass::method) 绑定成员函数
  • 成员变量用 .def_readwrite("name", &MyClass::name);只读用 def_readonly
  • 注意:C++ 对象生命周期由 Python 引用计数管理,除非显式用 pybind11::return_value_policy 控制,否则默认按值返回会触发拷贝

真正容易被忽略的是 Python 解释器初始化状态——如果你在已有 Python 进程中(比如嵌入式场景)加载 pybind11 模块,确保 PyEval_InitThreads()(旧版)或 GIL 管理已就绪;而绝大多数情况(直接运行 python script.py)不需要操心这个。