当前位置: 首页 > news >正文

【pybind11】 pybind11如何调用python

pybind11 学习指南

目录

  1. 什么是 pybind11
  2. 安装 pybind11
  3. 基础使用
  4. 数据类型转换
  5. 类和对象绑定
  6. NumPy 集成
  7. 异常处理
  8. 性能优化
  9. 编译和构建
  10. 实际应用案例
  11. 最佳实践

什么是 pybind11

pybind11 是一个轻量级的头文件库,用于在 C++ 和 Python 之间创建绑定。它的设计灵感来自 Boost.Python,但更简洁、现代化,且仅依赖 C++11 标准。

主要优势

1. 简洁性与易用性
  • 头文件库(Header-only):无需预编译,直接包含即可使用
  • CMake 集成简单:仅需 3 行代码即可集成到项目
  • 现代 C++ 语法:充分利用 C++11/14/17 特性
2. 自动化内存管理
  • RAII 支持:基于 RAII 和引用计数自动处理 Python 对象析构
  • 减少内存泄漏:相比原生 Python C API,无需手动 Py_DECREF
  • 智能指针集成:天然支持 std::shared_ptrstd::unique_ptr
3. 开发效率与易用性
  • 自动类型推导:自动处理 C++ 和 Python 类型转换
  • 直观的 API:类似于编写普通 C++ 代码
  • 丰富的文档:完善的文档和示例
4. 跨版本与生态兼容
  • Python 版本支持:同时支持 Python 2.7 和 3.x,兼容 PyPy
  • 科学计算库集成:与 NumPy、OpenCV 等库深度集成
  • 数据类型映射:提供 dtype 映射和缓冲区协议支持

主要局限性

1. 编译期依赖与复杂度
  • 编译器要求:强依赖 C++11 及以上(GCC ≥4.8, VS ≥2015)
  • 编译时间增加:模板实例化可能使大型项目编译时间增加 30%~50%
  • 旧项目升级成本:对于使用旧编译器的项目,升级成本较高
2. 调试与错误处理挑战
  • 模板错误信息复杂:类型不匹配时错误信息可长达千行
  • 混合调试困难:需要配置符号链接和混合模式调试器
  • 运行时错误定位:C++ 异常到 Python 异常的映射有时不够直观
3. 高级功能限制
  • 多继承支持不完善:需手动指定 py::multiple_inheritance 标记
  • 异步交互限制:无法直接在 C++ 线程中回调 Python 协程
  • GIL 和线程处理:复杂的多线程场景需要额外的 GIL 管理

安装 pybind11

方法一:使用 pip

pip install pybind11

方法二:使用 conda

conda install -c conda-forge pybind11

方法三:从源码编译

git clone https://github.com/pybind/pybind11.git
cd pybind11
mkdir build && cd build
cmake .. -DPYBIND11_TEST=OFF
make -j4
sudo make install

验证安装

import pybind11
print(pybind11.__version__)
print(pybind11.get_cmake_dir())

基础使用

最简单的示例

C++ 代码 (example.cpp)
#include <pybind11/pybind11.h>// 简单的 C++ 函数
int add(int i, int j) {return i + j;
}// 字符串处理函数
std::string greet(const std::string& name) {return "Hello, " + name + "!";
}// 定义 Python 模块
PYBIND11_MODULE(example, m) {m.doc() = "pybind11 example plugin";  // 模块文档字符串// 绑定函数m.def("add", &add, "A function which adds two numbers");m.def("greet", &greet, "A function which greets");
}
setup.py
from pybind11.setup_helpers import Pybind11Extension, build_ext
from pybind11 import get_cmake_dir
import pybind11# 定义扩展模块
ext_modules = [Pybind11Extension("example",["example.cpp"],include_dirs=[pybind11.get_include(),],language='c++'),
]# 安装配置
setup(name="example",ext_modules=ext_modules,cmdclass={"build_ext": build_ext},zip_safe=False,
)
编译和使用
# 编译
python setup.py build_ext --inplace# 使用
python -c "import example; print(example.add(1, 2)); print(example.greet('World'))"

参数和返回值

函数参数处理
#include <pybind11/pybind11.h>
#include <vector>
#include <string>// 默认参数
int power(int base, int exp = 2) {int result = 1;for (int i = 0; i < exp; ++i) {result *= base;}return result;
}// 关键字参数
void print_info(const std::string& name, int age, const std::string& city = "Unknown") {std::cout << "Name: " << name << ", Age: " << age << ", City: " << city << std::endl;
}// 可变参数
double average(const std::vector<double>& values) {if (values.empty()) return 0.0;double sum = 0.0;for (double val : values) {sum += val;}return sum / values.size();
}PYBIND11_MODULE(functions, m) {// 默认参数m.def("power", &power, "Calculate power with default exponent 2",py::arg("base"), py::arg("exp") = 2);// 关键字参数m.def("print_info", &print_info, "Print person information",py::arg("name"), py::arg("age"), py::arg("city") = "Unknown");// STL 容器自动转换m.def("average", &average, "Calculate average of a list");
}

数据类型转换

基本类型转换

pybind11 自动处理以下类型转换:

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>  // STL 容器支持// 基本类型
bool check_flag(bool flag) { return !flag; }
int process_int(int value) { return value * 2; }
double process_float(double value) { return value * 3.14; }
std::string process_string(const std::string& text) { return "Processed: " + text; 
}// STL 容器
std::vector<int> process_list(const std::vector<int>& input) {std::vector<int> result;for (int val : input) {result.push_back(val * 2);}return result;
}std::map<std::string, int> process_dict(const std::map<std::string, int>& input) {std::map<std::string, int> result;for (const auto& pair : input) {result[pair.first + "_processed"] = pair.second * 2;}return result;
}PYBIND11_MODULE(types, m) {m.def("check_flag", &check_flag);m.def("process_int", &process_int);m.def("process_float", &process_float);m.def("process_string", &process_string);m.def("process_list", &process_list);m.def("process_dict", &process_dict);
}

自定义类型转换

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>// 自定义结构体
struct Point {double x, y;Point(double x = 0, double y = 0) : x(x), y(y) {}double distance() const {return std::sqrt(x * x + y * y);}Point operator+(const Point& other) const {return Point(x + other.x, y + other.y);}
};// 处理自定义类型的函数
Point midpoint(const Point& p1, const Point& p2) {return Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}PYBIND11_MODULE(custom_types, m) {// 绑定自定义类型py::class_<Point>(m, "Point").def(py::init<>())  // 默认构造函数.def(py::init<double, double>())  // 带参数构造函数.def_readwrite("x", &Point::x)  // 可读写属性.def_readwrite("y", &Point::y).def("distance", &Point::distance)  // 方法.def("__add__", &Point::operator+)  // 运算符重载.def("__repr__", [](const Point& p) {  // 字符串表示return "Point(" + std::to_string(p.x) + ", " + std::to_string(p.y) + ")";});// 绑定函数m.def("midpoint", &midpoint, "Calculate midpoint of two points");
}

类和对象绑定

基本类绑定

#include <pybind11/pybind11.h>
#include <string>
#include <vector>class Calculator {
private:double value_;std::vector<double> history_;public:Calculator(double initial_value = 0.0) : value_(initial_value) {}// 基本运算void add(double x) { value_ += x; history_.push_back(value_);}void multiply(double x) { value_ *= x; history_.push_back(value_);}void clear() { value_ = 0.0; history_.clear();}// 属性访问double getValue() const { return value_; }void setValue(double value) { value_ = value; }const std::vector<double>& getHistory() const { return history_; }// 静态方法static Calculator createWithValue(double value) {return Calculator(value);}
};PYBIND11_MODULE(calculator, m) {py::class_<Calculator>(m, "Calculator").def(py::init<>())  // 默认构造函数.def(py::init<double>())  // 带参数构造函数.def("add", &Calculator::add, "Add a value").def("multiply", &Calculator::multiply, "Multiply by a value").def("clear", &Calculator::clear, "Clear calculator").def("getValue", &Calculator::getValue, "Get current value").def("setValue", &Calculator::setValue, "Set current value").def("getHistory", &Calculator::getHistory, py::return_value_policy::reference_internal)  // 返回引用.def_static("createWithValue", &Calculator::createWithValue)  // 静态方法// 属性语法糖.def_property("value", &Calculator::getValue, &Calculator::setValue).def_property_readonly("history", &Calculator::getHistory,py::return_value_policy::reference_internal);
}

继承和多态

#include <pybind11/pybind11.h>
#include <memory>// 基类
class Animal {
public:virtual ~Animal() = default;virtual std::string speak() const = 0;virtual std::string getType() const { return "Animal"; }void eat() { std::cout << "Eating..." << std::endl; }
};// 派生类
class Dog : public Animal {
private:std::string name_;public:Dog(const std::string& name) : name_(name) {}std::string speak() const override {return name_ + " says Woof!";}std::string getType() const override {return "Dog";}void wagTail() { std::cout << name_ << " is wagging tail!" << std::endl; }const std::string& getName() const { return name_; }
};class Cat : public Animal {
private:std::string name_;public:Cat(const std::string& name) : name_(name) {}std::string speak() const override {return name_ + " says Meow!";}std::string getType() const override {return "Cat";}void purr() { std::cout << name_ << " is purring!" << std::endl; }
};// 工厂函数
std::unique_ptr<Animal> createAnimal(const std::string& type, const std::string& name) {if (type == "dog") {return std::make_unique<Dog>(name);} else if (type == "cat") {return std::make_unique<Cat>(name);}return nullptr;
}PYBIND11_MODULE(animals, m) {// 基类py::class_<Animal>(m, "Animal").def("speak", &Animal::speak).def("getType", &Animal::getType).def("eat", &Animal::eat);// 派生类 - 注意指定基类py::class_<Dog, Animal>(m, "Dog")  // Dog 继承自 Animal.def(py::init<const std::string&>()).def("wagTail", &Dog::wagTail).def("getName", &Dog::getName);py::class_<Cat, Animal>(m, "Cat").def(py::init<const std::string&>()).def("purr", &Cat::purr);// 工厂函数m.def("createAnimal", &createAnimal, "Create an animal of specified type");
}

NumPy 集成

NumPy 数组处理

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>namespace py = pybind11;// 处理 1D NumPy 数组
py::array_t<double> multiply_array(py::array_t<double> input, double factor) {// 获取数组信息py::buffer_info buf_info = input.request();if (buf_info.ndim != 1) {throw std::runtime_error("Input array must be 1-dimensional");}// 创建输出数组auto result = py::array_t<double>(buf_info.size);py::buffer_info result_info = result.request();// 获取数据指针double* input_ptr = static_cast<double*>(buf_info.ptr);double* result_ptr = static_cast<double*>(result_info.ptr);// 处理数据for (size_t i = 0; i < buf_info.size; i++) {result_ptr[i] = input_ptr[i] * factor;}return result;
}// 处理 2D NumPy 数组
py::array_t<double> matrix_multiply(py::array_t<double> a, py::array_t<double> b) {py::buffer_info buf_a = a.request();py::buffer_info buf_b = b.request();if (buf_a.ndim != 2 || buf_b.ndim != 2) {throw std::runtime_error("Input arrays must be 2-dimensional");}if (buf_a.shape[1] != buf_b.shape[0]) {throw std::runtime_error("Incompatible matrix dimensions");}size_t M = buf_a.shape[0];size_t K = buf_a.shape[1];size_t N = buf_b.shape[1];// 创建结果矩阵auto result = py::array_t<double>({M, N});py::buffer_info buf_result = result.request();double* ptr_a = static_cast<double*>(buf_a.ptr);double* ptr_b = static_cast<double*>(buf_b.ptr);double* ptr_result = static_cast<double*>(buf_result.ptr);// 矩阵乘法for (size_t i = 0; i < M; i++) {for (size_t j = 0; j < N; j++) {double sum = 0;for (size_t k = 0; k < K; k++) {sum += ptr_a[i * K + k] * ptr_b[k * N + j];}ptr_result[i * N + j] = sum;}}return result;
}// 就地修改数组
void inplace_square(py::array_t<double> array) {py::buffer_info buf = array.request();double* ptr = static_cast<double*>(buf.ptr);for (size_t i = 0; i < buf.size; i++) {ptr[i] = ptr[i] * ptr[i];}
}// 创建数组
py::array_t<double> create_range(double start, double stop, size_t num) {auto result = py::array_t<double>(num);py::buffer_info buf = result.request();double* ptr = static_cast<double*>(buf.ptr);double step = (stop - start) / (num - 1);for (size_t i = 0; i < num; i++) {ptr[i] = start + i * step;}return result;
}PYBIND11_MODULE(numpy_ops, m) {m.def("multiply_array", &multiply_array, "Multiply array by scalar");m.def("matrix_multiply", &matrix_multiply, "Multiply two matrices");m.def("inplace_square", &inplace_square, "Square array elements in-place");m.def("create_range", &create_range, "Create evenly spaced array");
}

与科学计算库集成

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/eigen.h>  // 如果使用 Eigen
#include <vector>
#include <algorithm>
#include <numeric>namespace py = pybind11;// 统计函数
struct Statistics {static double mean(py::array_t<double> arr) {py::buffer_info buf = arr.request();double* ptr = static_cast<double*>(buf.ptr);double sum = 0.0;for (size_t i = 0; i < buf.size; i++) {sum += ptr[i];}return sum / buf.size;}static double std_dev(py::array_t<double> arr) {double m = mean(arr);py::buffer_info buf = arr.request();double* ptr = static_cast<double*>(buf.ptr);double sum_sq_diff = 0.0;for (size_t i = 0; i < buf.size; i++) {double diff = ptr[i] - m;sum_sq_diff += diff * diff;}return std::sqrt(sum_sq_diff / buf.size);}static py::array_t<double> normalize(py::array_t<double> arr) {double m = mean(arr);double std = std_dev(arr);py::buffer_info buf = arr.request();auto result = py::array_t<double>(buf.size);py::buffer_info result_buf = result.request();double* input_ptr = static_cast<double*>(buf.ptr);double* result_ptr = static_cast<double*>(result_buf.ptr);for (size_t i = 0; i < buf.size; i++) {result_ptr[i] = (input_ptr[i] - m) / std;}return result;}
};PYBIND11_MODULE(stats, m) {py::class_<Statistics>(m, "Statistics").def_static("mean", &Statistics::mean, "Calculate mean").def_static("std_dev", &Statistics::std_dev, "Calculate standard deviation").def_static("normalize", &Statistics::normalize, "Normalize array");
}

异常处理

C++ 异常到 Python 异常的映射

#include <pybind11/pybind11.h>
#include <stdexcept>
#include <string>namespace py = pybind11;// 自定义异常类
class CustomError : public std::exception {
private:std::string message_;public:CustomError(const std::string& message) : message_(message) {}const char* what() const noexcept override {return message_.c_str();}
};// 可能抛出异常的函数
double safe_divide(double a, double b) {if (b == 0.0) {throw std::invalid_argument("Division by zero is not allowed");}return a / b;
}int safe_array_access(const std::vector<int>& arr, size_t index) {if (index >= arr.size()) {throw std::out_of_range("Index " + std::to_string(index) + " is out of range for array of size " + std::to_string(arr.size()));}return arr[index];
}void validate_input(const std::string& input) {if (input.empty()) {throw CustomError("Input string cannot be empty");}if (input.length() > 100) {throw CustomError("Input string is too long (max 100 characters)");}
}// 文件操作示例
std::string read_file_content(const std::string& filename) {std::ifstream file(filename);if (!file.is_open()) {throw std::runtime_error("Could not open file: " + filename);}std::string content;std::string line;while (std::getline(file, line)) {content += line + "\n";}return content;
}PYBIND11_MODULE(exceptions, m) {// 注册自定义异常py::register_exception<CustomError>(m, "CustomError");// 绑定可能抛出异常的函数m.def("safe_divide", &safe_divide, "Safely divide two numbers", py::arg("a"), py::arg("b"));m.def("safe_array_access", &safe_array_access,"Safely access array element",py::arg("arr"), py::arg("index"));m.def("validate_input", &validate_input,"Validate input string");m.def("read_file_content", &read_file_content,"Read file content");
}

Python 中的异常处理

import exceptionstry:result = exceptions.safe_divide(10, 0)
except ValueError as e:print(f"ValueError: {e}")try:arr = [1, 2, 3, 4, 5]value = exceptions.safe_array_access(arr, 10)
except IndexError as e:print(f"IndexError: {e}")try:exceptions.validate_input("")
except exceptions.CustomError as e:print(f"CustomError: {e}")

性能优化

避免不必要的拷贝

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <vector>
#include <string>namespace py = pybind11;class DataProcessor {
private:std::vector<double> data_;public:// 构造函数 - 移动语义DataProcessor(std::vector<double>&& data) : data_(std::move(data)) {}// 返回引用 - 避免拷贝const std::vector<double>& getData() const { return data_; }// 就地修改 - 避免拷贝void multiplyInPlace(double factor) {for (auto& val : data_) {val *= factor;}}// 使用 const 引用参数void appendData(const std::vector<double>& new_data) {data_.insert(data_.end(), new_data.begin(), new_data.end());}// 返回视图而不是拷贝py::array_t<double> getAsArray() {return py::cast(data_);}
};// 高效的字符串处理
std::string process_large_string(const std::string& input) {// 预分配空间std::string result;result.reserve(input.size() * 2);for (char c : input) {result += c;if (c != ' ') {result += '_';}}return result;
}PYBIND11_MODULE(performance, m) {py::class_<DataProcessor>(m, "DataProcessor").def(py::init<std::vector<double>&&>()).def("getData", &DataProcessor::getData, py::return_value_policy::reference_internal)  // 返回引用.def("multiplyInPlace", &DataProcessor::multiplyInPlace).def("appendData", &DataProcessor::appendData).def("getAsArray", &DataProcessor::getAsArray);m.def("process_large_string", &process_large_string);
}

并行处理

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <thread>
#include <vector>
#include <future>namespace py = pybind11;// 并行数组处理
py::array_t<double> parallel_process_array(py::array_t<double> input, std::function<double(double)> func,size_t num_threads = 0) {py::buffer_info buf = input.request();if (num_threads == 0) {num_threads = std::thread::hardware_concurrency();}auto result = py::array_t<double>(buf.size);py::buffer_info result_buf = result.request();double* input_ptr = static_cast<double*>(buf.ptr);double* result_ptr = static_cast<double*>(result_buf.ptr);// 分块处理size_t chunk_size = buf.size / num_threads;std::vector<std::future<void>> futures;for (size_t t = 0; t < num_threads; ++t) {size_t start = t * chunk_size;size_t end = (t == num_threads - 1) ? buf.size : (t + 1) * chunk_size;futures.push_back(std::async(std::launch::async, [=]() {for (size_t i = start; i < end; ++i) {result_ptr[i] = func(input_ptr[i]);}}));}// 等待所有任务完成for (auto& future : futures) {future.wait();}return result;
}// 释放 GIL 的长时间运算
py::array_t<double> compute_intensive_task(py::array_t<double> input) {py::buffer_info buf = input.request();auto result = py::array_t<double>(buf.size);py::buffer_info result_buf = result.request();double* input_ptr = static_cast<double*>(buf.ptr);double* result_ptr = static_cast<double*>(result_buf.ptr);// 释放 GIL 进行计算密集型任务py::gil_scoped_release release;for (size_t i = 0; i < buf.size; ++i) {// 模拟复杂计算double val = input_ptr[i];for (int j = 0; j < 1000; ++j) {val = std::sin(val) + std::cos(val);}result_ptr[i] = val;}return result;
}PYBIND11_MODULE(parallel, m) {m.def("parallel_process_array", &parallel_process_array,"Process array in parallel",py::arg("input"), py::arg("func"), py::arg("num_threads") = 0);m.def("compute_intensive_task", &compute_intensive_task,"Compute intensive task with GIL release");
}

编译和构建

使用 CMake

CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(pybind11_project)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 查找 Python 和 pybind11
find_package(Python COMPONENTS Interpreter Development REQUIRED)
find_package(pybind11 REQUIRED)# 创建 pybind11 模块
pybind11_add_module(example example.cpp)# 编译定义
target_compile_definitions(example PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO})# 链接库(如果需要)
# target_link_libraries(example PRIVATE some_library)# 设置模块属性
set_target_properties(example PROPERTIESCXX_VISIBILITY_PRESET "hidden"INTERPROCEDURAL_OPTIMIZATION TRUE
)

使用 setup.py

高级 setup.py 配置
from pybind11.setup_helpers import Pybind11Extension, build_ext
from pybind11 import get_cmake_dir
import pybind11
from setuptools import setup, Extension
import os# 编译标志
extra_compile_args = []
extra_link_args = []# 根据平台设置编译选项
if os.name == 'nt':  # Windowsextra_compile_args += ['/O2', '/openmp']
else:  # Unix-likeextra_compile_args += ['-O3', '-fopenmp', '-march=native']extra_link_args += ['-fopenmp']# 定义扩展模块
ext_modules = [Pybind11Extension("advanced_module",["src/main.cpp","src/algorithms.cpp","src/data_structures.cpp",],include_dirs=[pybind11.get_include(),"include",  # 项目头文件目录"/usr/local/include",  # 系统头文件目录],libraries=["blas", "lapack"],  # 链接的库library_dirs=["/usr/local/lib"],define_macros=[("VERSION_INFO", '"dev"')],extra_compile_args=extra_compile_args,extra_link_args=extra_link_args,language='c++'),
]setup(name="advanced_pybind11_project",version="0.1.0",author="Your Name",author_email="your.email@example.com",description="Advanced pybind11 project",long_description="Detailed description of the project",ext_modules=ext_modules,cmdclass={"build_ext": build_ext},zip_safe=False,python_requires=">=3.6",install_requires=["numpy>=1.15.0",],
)

跨平台编译

Windows 特定配置
#ifdef _WIN32#define EXPORT __declspec(dllexport)
#else#define EXPORT
#endif// Windows 特定的代码
#ifdef _WIN32
#include <windows.h>void windows_specific_function() {// Windows API 调用
}
#endif
macOS/Linux 特定配置
#ifdef __APPLE__// macOS 特定代码
#elif __linux__// Linux 特定代码
#endif

实际应用案例

案例1:图像处理库

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <vector>
#include <algorithm>
#include <cmath>namespace py = pybind11;class ImageProcessor {
public:// 高斯模糊static py::array_t<uint8_t> gaussian_blur(py::array_t<uint8_t> image, double sigma) {auto buf = image.request();if (buf.ndim != 3 || buf.shape[2] != 3) {throw std::runtime_error("Expected 3D array with shape (H, W, 3)");}int height = buf.shape[0];int width = buf.shape[1];int channels = buf.shape[2];// 创建输出图像auto result = py::array_t<uint8_t>({height, width, channels});auto result_buf = result.request();uint8_t* input_ptr = static_cast<uint8_t*>(buf.ptr);uint8_t* output_ptr = static_cast<uint8_t*>(result_buf.ptr);// 创建高斯核int kernel_size = static_cast<int>(6 * sigma + 1);if (kernel_size % 2 == 0) kernel_size++;int half_kernel = kernel_size / 2;std::vector<double> kernel(kernel_size);double sum = 0.0;for (int i = 0; i < kernel_size; i++) {int x = i - half_kernel;kernel[i] = std::exp(-(x * x) / (2 * sigma * sigma));sum += kernel[i];}// 归一化核for (auto& k : kernel) {k /= sum;}// 应用模糊for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {for (int c = 0; c < channels; c++) {double value = 0.0;double weight_sum = 0.0;for (int ky = -half_kernel; ky <= half_kernel; ky++) {for (int kx = -half_kernel; kx <= half_kernel; kx++) {int ny = y + ky;int nx = x + kx;if (ny >= 0 && ny < height && nx >= 0 && nx < width) {double weight = kernel[ky + half_kernel] * kernel[kx + half_kernel];value += input_ptr[(ny * width + nx) * channels + c] * weight;weight_sum += weight;}}}output_ptr[(y * width + x) * channels + c] = static_cast<uint8_t>(std::round(value / weight_sum));}}}return result;}// 边缘检测static py::array_t<uint8_t> edge_detection(py::array_t<uint8_t> image) {auto buf = image.request();int height = buf.shape[0];int width = buf.shape[1];auto result = py::array_t<uint8_t>({height, width});auto result_buf = result.request();uint8_t* input_ptr = static_cast<uint8_t*>(buf.ptr);uint8_t* output_ptr = static_cast<uint8_t*>(result_buf.ptr);// Sobel 算子int sobel_x[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};int sobel_y[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};for (int y = 1; y < height - 1; y++) {for (int x = 1; x < width - 1; x++) {int gx = 0, gy = 0;for (int ky = -1; ky <= 1; ky++) {for (int kx = -1; kx <= 1; kx++) {int pixel = input_ptr[(y + ky) * width + (x + kx)];gx += pixel * sobel_x[ky + 1][kx + 1];gy += pixel * sobel_y[ky + 1][kx + 1];}}int magnitude = static_cast<int>(std::sqrt(gx * gx + gy * gy));output_ptr[y * width + x] = static_cast<uint8_t>(std::min(255, magnitude));}}return result;}
};PYBIND11_MODULE(image_processor, m) {m.doc() = "High-performance image processing library";py::class_<ImageProcessor>(m, "ImageProcessor").def_static("gaussian_blur", &ImageProcessor::gaussian_blur,"Apply Gaussian blur to image",py::arg("image"), py::arg("sigma")).def_static("edge_detection", &ImageProcessor::edge_detection,"Detect edges in grayscale image",py::arg("image"));
}

案例2:机器学习算法

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>
#include <vector>
#include <random>
#include <algorithm>
#include <cmath>namespace py = pybind11;class KMeans {
private:int k_;int max_iterations_;double tolerance_;std::vector<std::vector<double>> centroids_;std::vector<int> labels_;public:KMeans(int k, int max_iterations = 100, double tolerance = 1e-4): k_(k), max_iterations_(max_iterations), tolerance_(tolerance) {}void fit(py::array_t<double> X) {auto buf = X.request();if (buf.ndim != 2) {throw std::runtime_error("Expected 2D array");}int n_samples = buf.shape[0];int n_features = buf.shape[1];double* data = static_cast<double*>(buf.ptr);// 初始化聚类中心centroids_.resize(k_, std::vector<double>(n_features));labels_.resize(n_samples);std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<> dis(0, n_samples - 1);for (int i = 0; i < k_; i++) {int idx = dis(gen);for (int j = 0; j < n_features; j++) {centroids_[i][j] = data[idx * n_features + j];}}// K-means 迭代for (int iter = 0; iter < max_iterations_; iter++) {bool converged = true;// 分配点到最近的聚类中心for (int i = 0; i < n_samples; i++) {double min_dist = std::numeric_limits<double>::max();int best_cluster = 0;for (int j = 0; j < k_; j++) {double dist = 0.0;for (int f = 0; f < n_features; f++) {double diff = data[i * n_features + f] - centroids_[j][f];dist += diff * diff;}if (dist < min_dist) {min_dist = dist;best_cluster = j;}}if (labels_[i] != best_cluster) {converged = false;labels_[i] = best_cluster;}}// 更新聚类中心std::vector<std::vector<double>> new_centroids(k_, std::vector<double>(n_features, 0.0));std::vector<int> counts(k_, 0);for (int i = 0; i < n_samples; i++) {int cluster = labels_[i];counts[cluster]++;for (int f = 0; f < n_features; f++) {new_centroids[cluster][f] += data[i * n_features + f];}}for (int i = 0; i < k_; i++) {if (counts[i] > 0) {for (int f = 0; f < n_features; f++) {new_centroids[i][f] /= counts[i];}}}centroids_ = new_centroids;if (converged) break;}}std::vector<int> predict(py::array_t<double> X) {auto buf = X.request();int n_samples = buf.shape[0];int n_features = buf.shape[1];double* data = static_cast<double*>(buf.ptr);std::vector<int> predictions(n_samples);for (int i = 0; i < n_samples; i++) {double min_dist = std::numeric_limits<double>::max();int best_cluster = 0;for (int j = 0; j < k_; j++) {double dist = 0.0;for (int f = 0; f < n_features; f++) {double diff = data[i * n_features + f] - centroids_[j][f];dist += diff * diff;}if (dist < min_dist) {min_dist = dist;best_cluster = j;}}predictions[i] = best_cluster;}return predictions;}std::vector<std::vector<double>> get_centroids() const {return centroids_;}std::vector<int> get_labels() const {return labels_;}
};PYBIND11_MODULE(ml_algorithms, m) {py::class_<KMeans>(m, "KMeans").def(py::init<int, int, double>(),py::arg("k"), py::arg("max_iterations") = 100, py::arg("tolerance") = 1e-4).def("fit", &KMeans::fit, "Fit K-means clustering").def("predict", &KMeans::predict, "Predict cluster labels").def("get_centroids", &KMeans::get_centroids, "Get cluster centroids").def("get_labels", &KMeans::get_labels, "Get training labels");
}

最佳实践

1. 代码组织

// 项目结构建议
/*
project/
├── include/
│   ├── my_library/
│   │   ├── core.hpp
│   │   ├── algorithms.hpp
│   │   └── utils.hpp
│   └── pybind/
│       └── bindings.hpp
├── src/
│   ├── core.cpp
│   ├── algorithms.cpp
│   └── utils.cpp
├── pybind/
│   ├── main.cpp
│   ├── core_bindings.cpp
│   ├── algorithms_bindings.cpp
│   └── utils_bindings.cpp
├── tests/
│   ├── test_core.py
│   └── test_algorithms.py
├── CMakeLists.txt
└── setup.py
*/// 模块化绑定 - main.cpp
#include <pybind11/pybind11.h>namespace py = pybind11;// 声明各子模块的绑定函数
void bind_core(py::module& m);
void bind_algorithms(py::module& m);
void bind_utils(py::module& m);PYBIND11_MODULE(my_library, m) {m.doc() = "My high-performance library";// 绑定各个子模块bind_core(m);bind_algorithms(m);bind_utils(m);
}

2. 性能优化指南

// 避免的做法 ❌
py::list bad_function(py::list input) {py::list result;for (auto item : input) {// 频繁的 Python 对象操作result.append(item.cast<double>() * 2.0);}return result;
}// 推荐的做法 ✅
std::vector<double> good_function(const std::vector<double>& input) {std::vector<double> result;result.reserve(input.size());  // 预分配内存for (double val : input) {result.push_back(val * 2.0);  // 纯 C++ 操作}return result;  // 自动类型转换
}// 对于大数据处理,使用 NumPy 数组
py::array_t<double> best_function(py::array_t<double> input) {py::buffer_info buf = input.request();auto result = py::array_t<double>(buf.size);py::buffer_info result_buf = result.request();double* input_ptr = static_cast<double*>(buf.ptr);double* result_ptr = static_cast<double*>(result_buf.ptr);// 释放 GIL 进行密集计算py::gil_scoped_release release;for (size_t i = 0; i < buf.size; i++) {result_ptr[i] = input_ptr[i] * 2.0;}return result;
}

3. 错误处理最佳实践

#include <pybind11/pybind11.h>
#include <stdexcept>namespace py = pybind11;// 定义有意义的异常类型
class ValidationError : public std::invalid_argument {
public:ValidationError(const std::string& msg) : std::invalid_argument(msg) {}
};class ComputationError : public std::runtime_error {
public:ComputationError(const std::string& msg) : std::runtime_error(msg) {}
};// 输入验证函数
void validate_array_input(py::array_t<double> arr, const std::string& name) {if (arr.size() == 0) {throw ValidationError(name + " cannot be empty");}py::buffer_info buf = arr.request();if (buf.ndim > 2) {throw ValidationError(name + " must be 1D or 2D array");}
}// 安全的计算函数
py::array_t<double> safe_computation(py::array_t<double> input) {validate_array_input(input, "input");try {// 执行计算py::buffer_info buf = input.request();auto result = py::array_t<double>(buf.size);// ... 计算逻辑 ...return result;} catch (const std::exception& e) {throw ComputationError("Computation failed: " + std::string(e.what()));}
}PYBIND11_MODULE(safe_module, m) {// 注册自定义异常py::register_exception<ValidationError>(m, "ValidationError");py::register_exception<ComputationError>(m, "ComputationError");m.def("safe_computation", &safe_computation);
}

4. 文档和测试

// 良好的文档实践
PYBIND11_MODULE(documented_module, m) {m.doc() = R"pbdoc(A well-documented pybind11 moduleThis module provides high-performance algorithms for data processing.All functions are optimized for NumPy arrays and support parallel processing.)pbdoc";m.def("process_data", &process_data, R"pbdoc(Process input data using advanced algorithms.Parameters----------data : numpy.ndarrayInput data array (1D or 2D)algorithm : strAlgorithm to use ('fast', 'accurate', 'balanced')parallel : bool, optionalEnable parallel processing (default: True)Returns-------numpy.ndarrayProcessed data arrayRaises------ValueErrorIf data is empty or has invalid shapeRuntimeErrorIf computation failsExamples-------->>> import numpy as np>>> data = np.random.rand(1000)>>> result = process_data(data, 'fast'))pbdoc",py::arg("data"), py::arg("algorithm"), py::arg("parallel") = true);
}

5. 调试技巧

#include <pybind11/pybind11.h>
#include <iostream>// 调试宏
#ifdef DEBUG#define DBG_PRINT(x) std::cout << "[DEBUG] " << x << std::endl
#else#define DBG_PRINT(x)
#endif// 可选的详细输出
class VerboseCalculator {
private:bool verbose_;public:VerboseCalculator(bool verbose = false) : verbose_(verbose) {}double compute(double x) {if (verbose_) {std::cout << "Computing for input: " << x << std::endl;}double result = x * x + 2 * x + 1;if (verbose_) {std::cout << "Result: " << result << std::endl;}return result;}
};PYBIND11_MODULE(debug_module, m) {py::class_<VerboseCalculator>(m, "VerboseCalculator").def(py::init<bool>(), py::arg("verbose") = false).def("compute", &VerboseCalculator::compute).def_property("verbose", [](const VerboseCalculator& self) { /* getter */ },[](VerboseCalculator& self, bool v) { /* setter */ });
}

总结

pybind11 是一个强大而优雅的 C++/Python 绑定库,它显著简化了在两种语言之间创建接口的过程。通过本指南,您已经学会了:

主要收获:

  1. 基础绑定:函数、类、异常的绑定方法
  2. 高级特性:NumPy 集成、并行处理、内存管理
  3. 性能优化:避免拷贝、GIL 管理、编译优化
  4. 实际应用:图像处理、机器学习等真实场景
  5. 最佳实践:代码组织、错误处理、文档编写

关键要点:

  • 简洁性:相比其他绑定库,pybind11 提供了最简洁的 API
  • 性能:通过适当的优化技巧,可以获得接近原生 C++ 的性能
  • 易用性:自动类型转换和内存管理大大降低了使用难度
  • 生态系统:与 NumPy、SciPy 等科学计算库无缝集成

下一步建议:

  1. 从简单项目开始,逐步掌握各种特性
  2. 关注性能瓶颈,合理使用优化技巧
  3. 建立良好的测试和文档习惯
  4. 参与开源项目,学习实际应用经验

Happy coding with pybind11! 🚀

http://www.lryc.cn/news/599196.html

相关文章:

  • 《整合Spring Cache:本地缓存、Redis与Caffeine对比实践》
  • Python 数据可视化之 Matplotlib 库
  • 【国内电子数据取证厂商龙信科技】谁是躲在“向日葵”后的
  • OSPF之多区域
  • 【ResNet50图像分类部署至RK3588】模型训练→转换RKNN→开发板部署
  • Jmeter的元件使用介绍:(四)前置处理器详解
  • JMeter每次压测前清除全部以确保异常率准确(以黑马点评为例、详细图解)
  • Pytorch中register_buffer和torch.nn.Parameter的异同
  • npm init vite-app runoob-vue3-test2 ,npm init vue@latest,指令区别
  • LIMA:大语言模型对齐的“少即是多”革命——原理、实验与范式重构
  • VR 技术在污水处理领域的创新性应用探索​
  • 华为云DRS实现Oracle到GaussDB数据库迁移的全流程技术方案
  • GTSuite许可与网络安全
  • Android Studio 自带的官方模拟器,ABI这一列是x86_64,xABI这一列是arm64-v8a
  • Apache Ranger 权限管理
  • Android Studio 2024 内嵌 Unity 3D 开发示例
  • Android studio自带的Android模拟器都是x86架构的吗,需要把arm架构的app翻译成x86指令?
  • Oracle数据块8KB、OS默认认块管理4KB,是否需调整大小为一致?
  • 弹性网:基于神经网络的多组分磁共振弹性成像波反演与不确定性量化|文献速递-医学影像算法文献分享
  • LeetCode 127:单词接龙
  • Hive-vscode-snippets
  • Hive【Hive架构及工作原理】
  • Oracle MCP本地部署测试
  • js实现宫格布局图片放大交互动画
  • [python][flask]flask接受get或者post参数
  • 【调试Bug】网络在训练中输出NaN
  • 关于网络模型
  • 基于深度学习的图像分类:使用DenseNet实现高效分类
  • Lua(数据库访问)
  • 全新轻量化PHP网盘搜索引擎系统源码