pybind11适合有完整c++头文件、需高效绑定类与stl容器的场景;cython适合需混写python/c++逻辑或改造热点代码,但构建配置和内存管理更易出错。

pybind11 和 Cython 都能包装 C++ 代码,但它们解决的问题不在同一层——选错不是性能差一点的事,而是写到一半发现类型映射卡死、或者编译失败后查不出原因。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
用 pybind11 还是 Cython?先看你的 C++ 代码有没有现成的头文件
- 如果你有完整的 C++ 头文件(
.h或.hpp),且类定义清晰、不依赖私有模板实现细节,pybind11是更直接的选择 - 如果你没有头文件,只有已编译好的
.so/.dll,或者 C++ 接口本身是 C 风格(extern "C"),那Cython可以通过.pxd声明接口,但实际不如ctypes简单 - 如果你要把 Python 逻辑也往 C++ 侧迁移(比如把 hot loop 用 Cython 的
def→cdef重写),那Cython是唯一能混写的方案;pybind11不处理 Python 源码,只绑定已有 C++
pybind11 绑定 C++ 类时,哪些写法容易出错
- 忘记给构造函数加
py::init<...>()</...>,Python 侧会报TypeError: <strong>init</strong>() missing 1 required positional argument - 成员函数返回
std::string或std::vector时没启用 STL 支持:必须在编译时加-DPYBIND11_HAS_STD_STRING_VIEW(新版默认开),否则可能崩溃或乱码 - 把裸指针(如
MyClass*)直接暴露给 Python,而没用py::return_value_policy::reference或py::return_value_policy::copy显式声明生命周期策略,导致访问已释放内存 - 在 Python 中调用带默认参数的函数,但没在绑定时显式写
.def("func", &Cls::func, py::arg("x") = 42),Python 侧就看不到默认值
Cython 包装 C++ 时,最常被忽略的构建细节
-
.pyx文件里用cdef extern from "myclass.h"引入 C++ 头,但没在setup.py中设置language="c++",结果编译器按 C 解析,报一堆unknown type name 'class' - 使用
std::vector或std::shared_ptr时,没在cdef extern from *块里手动声明对应 STL 类型,Cython 不会自动识别,只能靠cppclass手动补全 - 混用 Python 字符串和
std::string:Cython 默认把bytes当char<em></em>,把str(Unicode)当PyObject,想自动转std::string得额外写转换逻辑,否则运行时报TypeError: expected bytes, got str
真正卡住人的地方往往不是语法,而是“谁负责内存”这件事没对齐:pybind11 默认按 C++ 对象语义管理生命周期,Cython 默认按 Python 对象语义——如果你的 C++ 类内部持有资源(如文件句柄、GPU 显存),又没写好析构绑定,Python 侧 del obj 后资源不释放,问题会延迟暴露。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










