使用 Pybind11 集成 C++ 与Python的方法
pybind11 的作用
鉴于 Python 脚本的运行速度相对较慢,而 C++ 具有显著的性能优势,Pybind11 成为了解决 Python 性能瓶颈的关键工具。它允许 Python 代码无缝调用高性能的 C++ 程序,从而大幅提升脚本的执行效率。此外,Pybind11 还提供了双向互操作性,支持 C++ 程序反向调用 Python 脚本,实现了两种语言的灵活集成。
使用 pybind11 的方法
在项目中集成 Pybind11 库通常有两种方法,它们主要区别在于如何管理 Pybind11 的依赖和 CMake 的配置方式。
方式一:源码引入 (Git Submodule 或 add_subdirectory)
此方法将 Pybind11 源码直接纳入你的项目结构中。
优点
- 版本锁定: 可以精确控制和锁定 Pybind11 的版本。
- 无外部依赖: 编译时不需要依赖环境中的 Pybind11 包。
操作步骤和修正
- 获取源码: 推荐使用更通用的 HTTPS 链接,并将源码直接克隆到目标目录。
# 1. 创建 extern 文件夹
mkdir extern
# 2. 克隆源码到目标路径
git clone https://github.com/pybind/pybind11.git extern/pybind11
在 CMake 中集成(关键步骤): 必须在你的 CMakeLists.txt 中使用 add_subdirectory 来包含 Pybind11 提供的 CMake 模块。
# CMakeLists.txt
# 引入 Pybind11 源码
add_subdirectory(extern/pybind11)
# 正常使用 pybind11_add_module 函数进行编译
pybind11_add_module(my_matrix matrix_bind.cpp matrix.cpp)
方式二:包管理器安装 (PyPI 或 Conda)
此方法将 Pybind11 作为环境依赖安装,通过 CMake 查找已安装的库。
优点
- 环境统一管理: 版本由包管理器维护(尤其是 Conda)。
- 依赖健全: 使用 Conda-forge 安装时,能更好地处理 C/C++ 运行时库依赖(例如解决
GLIBCXX冲突)。
操作步骤和修正
- 安装 Pybind11 包:
# 强烈推荐在 Conda 环境中使用 conda-forge 频道
conda install -c conda-forge pybind11
# 或者使用 pip
# pip install pybind11
在 CMake 中集成(关键步骤): 必须使用 find_package 指令告知 CMake 查找环境中已安装的 Pybind11。
# CMakeLists.txt
# 查找环境中已安装的 pybind11 库配置
find_package(pybind11 CONFIG REQUIRED)
# 正常使用 pybind11_add_module 函数
pybind11_add_module(my_matrix matrix_bind.cpp matrix.cpp)
本篇文章选择方式二使用 pybind11 库。
编写一个简单的 pybind11 例子
编写 C++ 程序
要想在 python 中调用 C++ 代码,首先我们需要有一个 C++ 程序。
简单编写一个加法程序:
/**
* @file add.cpp
* @brief 简单的 pybind11 示例:将一个 C++ 函数导出为 Python 模块
*
* 说明:
* - 使用 pybind11 将一个原生 C++ 函数导出为名为 "add" 的 Python 扩展模块。
* - 模块导出一个函数 add(int, int),用于返回两个整数的和。
* - 本文件展示了最小化的导出流程,便于学习 pybind11 的使用方法。
*
* 使用示例(Python):
* import add
* result = add.add(1, 2) # result == 3
*
* 设计细节:
* - 函数签名:int add(int i, int j)
* 返回两个整数的和,函数本身不抛出异常,也不进行边界检查(简单示例)。
*
* - PYBIND11_MODULE(add, module)
* 该宏定义了模块初始化函数并将模块命名为 "add"(在 Python 中通过 import add 引入)。
* 在模块初始化体内:
* - 设置可选模块文档字符串 module.doc()
* - 使用 module.def("add", &add, "A function which adds two numbers") 将 C++ 函数绑定为 Python 可调用对象
*/
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(add, module) {
module.doc() = "pybind11 example plugin"; //optional module docstring
module.def("add", &add, "A function which adds two numbers");
}
编写 CMakeLists.txt
# 最小 CMake 版本要求
cmake_minimum_required(VERSION 3.14)
# 定义项目名称为 'add',并指定项目使用 C++ 语言。
project(add LANGUAGES CXX)
# 查找 Python 3 环境。
# COMPONENTS Interpreter Development: 确保找到 Python 解释器和开发头文件/库。
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
# 查找 pybind11 库。
# REQUIRED: 如果找不到 pybind11,则停止配置并报错。
find_package(pybind11 REQUIRED)
# 设置 C++ 语言标准为 C++11。pybind11 通常至少需要 C++11。
set(CMAKE_CXX_STANDARD 11)
# 强制要求编译器必须支持设置的 C++ 标准
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 设置默认构建类型为 Release。这会启用优化选项。
set(CMAKE_BUILD_TYPE Release)
# 为 Release 构建类型设置高级优化标志 (-O3)
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
# 使用 pybind11 提供的便捷函数来创建 Python 扩展模块。
# 目标名: add (对应于 Python 中 import add)
# 源文件: add.cpp
# pybind11 会自动处理链接 Python 库和添加必要的包含目录。
pybind11_add_module(add add.cpp)**
拥有这两个文件就可以开始编译了!
编译出可以被 python 调用的 C++ add 函数
在上面两个文件的同级文件夹中进行如下操作
mkdir build
cd build
cmake ../
make -j`nproc`
现在在 build 文件夹中生成了一个动态链接库文件:add.cpython-313-aarch64-linux-gnu.so
| 组件 | 含义 | 说明 |
|---|---|---|
add |
模块名 | 这是在 pybind11_add_module(add ...) 中定义的模块名。在 Python 中会通过 import add 导入它。 |
.cpython |
ABI 标签(解释器) | 表示这是为 CPython(标准 Python 解释器)构建的扩展模块。 |
-313 |
ABI 标签(版本) | 表示该模块是针对 Python 3.13 的 ABI 编译的,只能被 Python 3.13 或兼容 ABI 的版本加载。 |
-aarch64 |
平台/架构 | 表示该模块是为 ARM 64 位架构(例如 Raspberry Pi 64 位、Arm 服务器)构建的。 |
-linux-gnu |
操作系统/发行版 | 表示使用 GNU 工具链 在 Linux 系统上构建的。 |
.so |
扩展名 | Linux/Unix 系统中的共享库(Shared Object),类似 Windows 的 .dll、macOS 的 .dylib。 |
之后我们可以在 python 环境中调用这个动态链接库。 |
||
在 build 文件夹中创建 add.py 文件 |
import add
a = 10
b = 20
c = add.add(a, b)
print(f"{a} + {b} = {c}")
执行这个文件就能看到使用 C++ 编写的 add 函数被成功调用了!
文件结构:
.
├── add.cpp
├── build
│ ├── add.cpython-313-aarch64-linux-gnu.so
│ ├── add.py
│ ├── CMakeCache.txt
│ ├── CMakeFiles
│ ├── cmake_install.cmake
│ └── Makefile
├── CMakeLists.txt
├── extern
│ └── pybind11
└── __pycache__
└── add.cpython-313.pyc

浙公网安备 33010602011771号