pybind11 概述

pybind11 概述#

pybind11 是轻量级的头文件库,它可以在 Python 和 C++之间暴露类型,主要用于创建现有 C++代码的 Python 绑定。它的目标和语法与 David Abrahams 编写的优秀的 Boost.Python 库相似:通过编译时内省推断类型信息,以减少传统扩展模块中的模板代码。

Boost.Python 的主要问题——也是创建类似项目的原因——是 Boost。Boost 是庞大而复杂的工具库套件,几乎适用于所有现有的 C++编译器。这种兼容性是有代价的:需要使用晦涩的模板技巧和解决方案来支持最古老和最容易出现错误的编译器版本。现在 C++11 兼容的编译器已经广泛可用,这种重型机械已经成为过于庞大且不必要的依赖项。

将这个库视为精简版的 Boost.Python,去除了所有与绑定生成无关的内容。去掉注释后,核心头文件仅需约 4K 行代码,并依赖于 Python(CPython 3.8+、PyPy 或 GraalPy)和 C++标准库。这种紧凑的实现得益于一些 C++11 语言特性(具体包括:元组、lambda 函数和可变参数模板)。自创建以来,这个库在许多方面已经超越了 Boost.Python,使得在许多常见情况下绑定代码大大简化。

核心特性#

pybind11 可以将以下核心 C++特性映射到 Python:

  • 接受和返回自定义数据结构的函数,支持按值、引用或指针传递

  • 实例方法和静态方法

  • 重载函数

  • 实例属性和静态属性

  • 任意异常类型

  • 枚举类型

  • 回调函数

  • 迭代器和范围

  • 自定义运算符

  • 单继承和多继承

  • STL 数据结构

  • 带引用计数的智能指针,如 std::shared_ptr

  • 带正确引用计数的内部引用

  • C++类中带有虚函数(和纯虚函数)可以在 Python 中扩展

  • 集成了 NumPy 支持(NumPy 2 需要 pybind11 2.12+)

工具#

除了核心功能外,pybind11 还提供了一些额外的便利功能:

  • 支持 CPython 3.8+、PyPy3 7.3.17+和 GraalPy 24.1+,具有与实现无关的接口(有关旧版 CPython 和 PyPy 版本的实现,请参考旧版本)。

  • 可以绑定带有捕获变量的 C++11 lambda 函数。lambda 捕获的数据存储在生成的 Python 函数对象内部。

  • pybind11 尽可能使用 C++11 的移动构造函数和移动赋值运算符,以高效地传递自定义数据类型。

  • 可以通过 Python 的缓冲区协议暴露自定义数据类型的内部存储。这例如在 Eigen 和 NumPy 等 C++矩阵类之间进行快速转换时很有用,而无需进行昂贵的复制操作。

  • 可以自动向量化函数,以便它们透明地应用于一个或多个 NumPy 数组参数的所有条目。

  • Python 的切片访问和赋值操作只需几行代码即可支持。

  • 所有内容都包含在几个头文件中;无需链接任何额外的库。

  • 与 Boost.Python 生成的等效绑定相比,二进制文件通常至少小两倍。最近对 PyRosetta(庞大的 Boost.Python 绑定项目)的 pybind11 转换报告了二进制文件大小减少 5.4 倍,编译时间减少 5.8 倍。

  • 函数签名在编译时预先计算(使用 constexpr ),导致二进制文件更小。

  • 只需稍加努力,C++类型就可以像普通 Python 对象一样被序列化和反序列化。

支持的编译器#

  • Clang/LLVM 3.3 或更新版本(对于 Apple Xcode 的 clang,这是 5.0.0 或更新版本)

  • GCC 4.8 或更新版本

  • Microsoft Visual Studio 2022 或更新版本(2019 可能可用,但在持续集成中已弃用)

  • 英特尔经典 C++编译器 18 或更新版本(在持续集成中测试了 ICC 20.2)

  • Cygwin/GCC(先前在 2.5.1 上测试过)

  • NVCC(在持续集成中测试了 CUDA 11.0)

  • NVIDIA PGI(在持续集成中测试了 20.9)