使用 pybind11 在 Python 和 C++ 之间互相访问数组数据
pybind11 提供了多种方式来实现 Python 和 C++ 之间的数组数据交换。以下是几种常见的方法:
1. 使用 NumPy 数组 (推荐)
pybind11 可以很好地与 NumPy 数组交互,这是最常用的方法。
安装准备
确保已安装:
- pybind11
- NumPy
示例代码
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>namespace py = pybind11;// C++ 函数:接收 NumPy 数组并处理
void process_array(py::array_t<double> input) {// 获取数组的 buffer infopy::buffer_info buf = input.request();// 检查数组维度if (buf.ndim != 1)throw std::runtime_error("Number of dimensions must be 1");// 获取原始指针double *ptr = static_cast<double *>(buf.ptr);// 处理数组数据for (size_t i = 0; i < buf.shape[0]; i++) {ptr[i] *= 2; // 示例操作:每个元素乘以2}
}// C++ 函数:返回 NumPy 数组
py::array_t<double> create_array(size_t size) {// 创建未初始化的数组py::array_t<double> result(size);// 获取 buffer info 和指针py::buffer_info buf = result.request();double *ptr = static_cast<double *>(buf.ptr);// 初始化数组for (size_t i = 0; i < size; i++) {ptr[i] = static_cast<double>(i); // 填充数据}return result;
}PYBIND11_MODULE(example, m) {m.def("process_array", &process_array, "Process a NumPy array");m.def("create_array", &create_array, "Create a NumPy array");
}
Python 端使用
import example
import numpy as np# 创建 NumPy 数组
arr = np.array([1.0, 2.0, 3.0], dtype=np.float64)# 传递给 C++ 处理
example.process_array(arr)print(arr) # 输出: [2. 4. 6.]# 从 C++ 获取数组
new_arr = example.create_array(5)
print(new_arr) # 输出: [0. 1. 2. 3. 4.]
2. 使用 STL 容器 (如 std::vector)
pybind11 也支持自动转换 std::vector 和 Python list。
示例代码
#include <pybind11/pybind11.h>
#include <pybind11/stl.h> // 提供 STL 容器支持
#include <vector>namespace py = pybind11;// 接收 vector 并返回处理后的 vector
std::vector<double> process_vector(const std::vector<double>& input) {std::vector<double> output;for (auto item : input) {output.push_back(item * 2);}return output;
}PYBIND11_MODULE(example, m) {m.def("process_vector", &process_vector, "Process a vector");
}
Python 端使用
import examplelst = [1.0, 2.0, 3.0]
result = example.process_vector(lst)
print(result) # 输出: [2.0, 4.0, 6.0]
3. 使用 Eigen 矩阵 (科学计算常用)
如果你使用 Eigen 库进行线性代数运算,pybind11 也提供了支持。
示例代码
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <Eigen/Dense>namespace py = pybind11;// 接收 Eigen 矩阵并处理
Eigen::MatrixXd process_matrix(const Eigen::MatrixXd& mat) {return mat * 2;
}PYBIND11_MODULE(example, m) {m.def("process_matrix", &process_matrix, "Process an Eigen matrix");
}
Python 端使用
import example
import numpy as npmat = np.array([[1.0, 2.0], [3.0, 4.0]])
result = example.process_matrix(mat)
print(result)
4. 直接内存访问 (高级用法)
对于需要高性能的场景,可以使用直接内存访问。
示例代码
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>namespace py = pybind11;// 直接操作内存
void direct_memory_access(py::array_t<double> input) {py::buffer_info buf = input.request();if (buf.ndim != 1)throw std::runtime_error("Number of dimensions must be 1");// 获取可写指针double *ptr = static_cast<double *>(buf.ptr);// 直接操作内存for (size_t i = 0; i < buf.shape[0]; i++) {ptr[i] += 1.0;}
}PYBIND11_MODULE(example, m) {m.def("direct_memory_access", &direct_memory_access, "Direct memory access to NumPy array");
}
注意事项
- 内存所有权:确保理解谁拥有内存的所有权,避免悬垂指针
- 线程安全:Python 的 GIL 会影响多线程操作
- 数据类型匹配:确保 C++ 和 Python 端的数据类型一致
- 数组连续性:某些操作需要数组是连续的,可以使用
input.require()
确保连续性
构建方法
使用 CMake 构建:
cmake_minimum_required(VERSION 3.4)
project(example)find_package(pybind11 REQUIRED)
find_package(Python REQUIRED COMPONENTS Development NumPy)pybind11_add_module(example example.cpp)
以上方法涵盖了大多数 Python 和 C++ 之间交换数组数据的场景,选择哪种方法取决于你的具体需求和性能要求。