c++如何使用pybind11绑定Python_c++无缝集成Python模块【跨语言】

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

#include 

int 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 构建(推荐方式):

  • 创建 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::vectorstd::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 就真像同一个语言里写的。