pybind11 本质是构建 C++ 与 Python 间自然、安全的胶水层;需正确配置环境、绑定函数/类、处理 STL 容器、异常及 GIL,模块名、返回策略等细节决定调用成败。

用 pybind11 把 C++ 代码暴露给 Python,本质是写一个“胶水层”:C++ 实现核心逻辑,Python 负责调用、组合和快速迭代。关键不是“能不能调”,而是“怎么调得自然、安全、不掉坑”。
一、环境准备与最简绑定
确保已安装 C++ 编译器(如 GCC/Clang/MSVC)和 Python(推荐 3.7+)。pybind11 是 header-only 库,不用编译,直接下载头文件或用包管理器(如 pip install pybind11)即可。
写一个最简 C++ 文件 example.cpp:
#includeint add(int a, int b) { return a + b; }
// PYBIND11_MODULE 名字必须和编译出的 .so/.pyd 文件名一致(不含后缀) PYBIND11_MODULE(example, m) { m.doc() = "pybind11 example plugin"; m.def("add", &add, "A function that adds two integers"); }
用 CMake 构建(推荐方式):
立即学习“Python免费学习笔记(深入)”;
- 创建 CMakeLists.txt,启用 C++14 或更高标准
- 调用
find_package(pybind11 REQUIRED) - 用
pybind11_add_module(example example.cpp)自动处理编译选项和输出路径
构建后生成 example.cpython-*.so(Linux/macOS)或 example.pyd(Windows),放到 Python 可导入路径下,就能直接 import example 并调用 example.add(2, 3)。
二、绑定类与成员函数
绑定 C++ 类时,pybind11 默认只暴露 public 成员,且需显式声明构造函数、属性和方法。
struct Pet {
Pet(const std::string &name) : name(name) { }
void set_name(const std::string &n) { name = n; }
const std::string &get_name() const { return name; }
std::string name;
};
PYBIND11MODULE(pet, m) {
pybind11::class(m, "Pet")
.def(pybind11::init())
.def_readwrite("name", &Pet::name)
.def("set_name", &Pet::set_name)
.def("get_name", &Pet::get_name);
}
Python 中可这样用:
from pet import Pet
p = Pet("Molly")
print(p.name) # Molly
p.name = "Max"
print(p.get_name()) # Max
注意:def_readwrite 绑定的是 C++ 成员变量,若需自定义 getter/setter,改用 def_property;若想支持 Python 的 __str__,加 .def("__str__", [](const Pet &p) { return "Pet[" + p.name + "]"; })。
三、处理常见类型与 STL 容器
pybind11 自动支持基本类型(int, double, std::string)、std::vector、std::map 等。但需注意:
-
std::vector会自动转为 Python list,反之亦然 - 若函数返回
std::vector(引用),默认会拷贝——如需零拷贝(只读),用& pybind11::return_value_policy::reference - 绑定
std::shared_ptr时,pybind11 会自动管理生命周期;裸指针慎用,易悬空 - 自定义类型需先绑定,再在容器中使用(如
std::vector要求Pet已绑定)
四、异常、GIL 与线程安全
从 C++ 抛出的 std::runtime_error 等异常,pybind11 会自动转为 Python RuntimeError;也可用 pybind11::value_error 映射为 ValueError。
涉及耗时计算时,建议释放 GIL(Global Interpreter Lock),让 Python 线程不被阻塞:
m.def("heavy_computation", [](int n) {
pybind11::gil_scoped_release release; // 释放 GIL
int result = do_heavy_work(n); // 纯 C++ 计算
pybind11::gil_scoped_acquire acquire; // 恢复 GIL(返回前必需)
return result;
});
多线程调用时,确保 C++ 侧本身线程安全(如避免共享未加锁的全局状态)。
基本上就这些。核心是:C++ 写好接口,pybind11 帮你“翻译”成 Python 可懂的语言,不复杂但容易忽略细节——比如模块名、返回策略、GIL 控制。用熟了,C++ 和 Python 就真像同一个语言里写的。











