百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

PyBind11简明教程【Python/C++】

bigegpt 2025-01-08 11:20 6 浏览

从 Python 调用 C++ 基本上有两种方法:使用 PyBind11 C++ 库生成 Python 模块,或使用 cytpes Python 包访问已编译的共享库。 使用 PyBind11 我们可以更轻松地共享许多数据类型,而使用 ctypes 是一种低级 C 风格的解决方案。

在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 3D场景编辑器

就我而言,我希望能够利用 C++ 的性能和可移植性,但我不想放弃解释语言的交互性以进行快速探索和调试。

幸运的是,从 Python 调用 C++ 并不像乍看起来那么困难。 这样,我们就可以在开发 C++ 代码的同时掌握 Python 的一些交互性。

就我而言,我想使用 Python 来:

  • 将一些问题参数传递给C++
  • 调用 C++ 代码来运行计算密集型例程
  • 检索最终结果以及一些用于调试的中间计算。
  • 以交互方式探索结果,并生成图表和报告。

使用 ctypes 的问题是共享许多数据类型需要大量的低级解决方法。 例如,虽然 ctypes 不支持复数等基本内容,但 PyBind11 使 Numpy 能够与 Eigen 完全互操作,并且需要最少的代码。

不过,我也发现了 PyBind11 的一个小问题。 事实证明,重新编译 C++ 代码并尝试重新加载 PyBind 生成的 Python 模块后,什么也没发生。 重新加载已编译模块的唯一方法是重新启动我的 Python 会话。 无论如何,这不是什么大问题,因为 Python 的启动时间可以忽略不计。 也许,这个步骤可以在 IDE 级别自动化。

因此,现在的问题是如何充分利用 PyBind11。

1、与 PyBind11 共享 C++ 类

PyBind11 的官方文档非常好,我能够毫无问题地开始使用它。 然而,我想分享这个库的超级快速入门指南,以及我打算如何使用它。

Pybind11 是一个仅包含头文件的库,你可以通过以下方式获取它:

pip install pybind11

虽然没有必要将所有 C++ 代码构造为类,但如果你有一个要在 C++ 和 Python 之间共享的类,Pybind11 将使事情变得非常容易。 实际上,我更像是一个struct向量类型的人,我总是想在给定的项目中引入最少数量的类。

然而,在这种情况下,我发现使用外观设计模式(参见 wiki)可以同时带来非常简单的 Python/C++ 互操作性和良好的 API。

所以,我设计了一个简单的类。 它基本上包含:

  • 读取问题参数的构造函数。
  • 执行计算的 run() 函数。
  • 一些 Eigen 数组作为公共变量来存储结果。

这是我的最小示例:

// mylib.h
#include <Eigen/Dense>
#include <cmath>

using Eigen::Matrix, Eigen::Dynamic;
typedef Matrix<std::complex<double>, Eigen::Dynamic, Eigen::Dynamic> myMatrix;

class MyClass {

    int N;
    double a;
    double b;

public:

    Eigen::VectorXd v_data;
    Eigen::VectorXd v_gamma;

    MyClass(){}
    MyClass( double a_in, double b_in, int N_in) 
    {
        N = N_in;
        a = a_in;
        b = b_in;
    }

    void run() 
    { 
        v_data = Eigen::VectorXd::LinSpaced(N, a, b); 

        auto gammafunc = [](double it) { return std::tgamma(it); };
        v_gamma = v_data.unaryExpr(gammafunc);
    }
};

为了共享这个类,我们需要添加一些 C++ 代码。 我倾向于在一个单独的文件中执行此操作,其中包含创建 python 包装器所需的所有内容:

// pywrap.cpp
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include "mylib.h"

namespace py = pybind11;
constexpr auto byref = py::return_value_policy::reference_internal;

PYBIND11_MODULE(MyLib, m) {
    m.doc() = "optional module docstring";

    py::class_<MyClass>(m, "MyClass")
    .def(py::init<double, double, int>())  
    .def("run", &MyClass::run, py::call_guard<py::gil_scoped_release>())
    .def_readonly("v_data", &MyClass::v_data, byref)
    .def_readonly("v_gamma", &MyClass::v_gamma, byref)
    ;
}

有几点需要强调:

  • 类构造函数签名由 .def(py::init<int, double, double>()) 指定
  • 对于 run() 函数,我们要求释放 GIL(全局解释器锁),这将阻止我们的函数使用多个线程。
  • 最后,可以使用以下 CMakeLists.txt 文件进行编译:
cmake_minimum_required(VERSION 3.10)

project(MyLib)
set(CMAKE_CXX_STANDARD 20)
set(PYBIND11_PYTHON_VERSION 3.6)
set(CMAKE_CXX_FLAGS "-Wall -Wextra -fPIC")

find_package(pybind11 REQUIRED)
find_package(Eigen3 REQUIRED)

pybind11_add_module(${PROJECT_NAME} pywrap.cpp)

target_compile_definitions(${PROJECT_NAME} PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO})
target_include_directories(${PROJECT_NAME} PRIVATE ${PYBIND11_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE Eigen3::Eigen)

现在已准备好了。 如果你使用 VS Code,配置 CMake 扩展后,只需按 F7 即可编译 C++ 库。

2、从 Python 调用 C++ 库

这个过程非常简单,并且应该开箱即用。 然而,有一些步骤可以优化交互式工作流程,这些步骤稍微棘手,但也值得实施。

例如,如果正在执行 Python 环境并且编译的库进入构建目录,可以执行以下操作:

import sys
sys.path.append("build/")
from MyLib import MyClass

import matplotlib.pyplot as plt

Simulation = MyClass(-4,4,1000)
Simulation.run()

plt.plot(Simulation.v_data, Simulation.v_gamma, \
"--", linewidth = 3, color=(1,0,0,0.6),label="Function Value")
plt.ylim(-10,10)
plt.xlabel("x")
plt.ylabel("($f(x) = \gamma(x)$)")
plt.title("(Gamma Function: $\gamma(z) = \int_0^\infty x^{z-1} e^{-x} dx$)",fontsize = 18);
plt.show()

结果如下:

请注意,特征向量会自动转换为 Python 数组。

修改 myLib.hpp 后,只需在 pywrap.cpp 中为我们要公开的每个新函数或变量添加一行代码。

不幸的是,这不会导致完全交互式的工作流程。 当你在更改后重新编译 C++ 代码时,Python 端不会发生任何事情。 即使尝试使用 importtools 重新加载 Python 模块:

import importlib
importlib.reload(MyLib)

什么都没发生。 原因是编译后的代码无法在Python中重新加载。

因此,使用 PyBind11 时,每次重新编译 C++ 代码时都需要重新启动 Python 会话,这对于开发目的来说有点烦人。 然而,这是一个很小的代价,因为 Python 的启动时间可以忽略不计,并且可能有一种方法可以使用一些 IDE 热键或其他工具来自动化该过程。

3、结束语

好了,这就是可以轻松地从 Python 调用 C++ 库的方法。教程的示例代码可以从github获取。

特别是,这个两步过程可以产生非常交互式的开发工作流程。 尽管我们有一个编辑-编译-运行工作流程,但我们在最后添加了一个解释器,所以现在我们的工作流程看起来像编辑-编译-运行-探索。

将来,我计划将两个功能合并到此工作流程中:

  • 第一个是 C++20 模块,它应该可以加快大型 C++ 项目的编译时间。 不幸的是,CMake 仍然与模块不兼容(请参阅此问题以获取更新),并且显然必须依赖像 Ninja-Build 这样的构建系统才能立即使用此功能。
  • 另一件事是修复重新编译 C++ 代码后(手动)重新启动 Python 会话的需要。 为此,我希望也许可以在 VSCode 级别对此采取一些措施。 到目前为止,VS Code 中的最佳选项似乎是终止 Python 会话,然后使用 Shift+Enter 执行 Python 代码,如果尚未打开会话,则会创建一个新会话。

原文链接:http://www.bimant.com/blog/pybind11-crash-tutorial/

相关推荐

5分钟搭建公网https网页文件服务器,免费权威TLS证书

请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习前言本文主要讲解如何快速搭建一个https网页文件服务器,并免费申请权威机构颁发的tls证...

nginx负载均衡配置(nginx负载均衡配置两个程序副本)

Nginx是什么没有听过Nginx?那么一定听过它的“同行”Apache吧!Nginx同Apache一样都是一种WEB服务器。基于REST架构风格,以统一资源描述符(UniformResources...

19《Nginx 入门教程》Nginx综合实践

今天我们将基于Nginx完成两个比较有用的场景,但是用到的Nginx的配置非常简单。内部Yum源搭建内部Pip源搭建1.实验环境ceph1centos7.6内网ip:172.16....

Nginx性能调优与优化指南(nginx优化配置大全)

Nginx性能调优需要结合服务器硬件资源、业务场景和负载特征进行针对性优化。以下是一些关键优化方向和具体配置示例:一、Nginx配置优化1.进程与连接数优化nginxworker_process...

C++后端开发必须彻底搞懂Nginx,从原理到实战(高级篇)

本文为Nginx实操高级篇。通过配置Nginx配置文件,实现正向代理、反向代理、负载均衡、Nginx缓存、动静分离和高可用Nginx6种功能,并对Nginx的原理作进一步的解析。当需...

【Nginx】史上最全的Nginx配置详解

Nginx服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里,http块又包括http全局块和server块。Nginx是非常重要的负载均衡中间件,被广泛应用于大型...

【Nginx】Nginx 4种常见配置实例(nginx基本配置与参数说明)

本文主要介绍nginx4种常见的配置实例。Nginx实现反向代理;Nginx实现负载均衡;Nginx实现动静分离;Nginx实现高可用集群;Nginx4种常见配置实例如下:一、Nginx反向代理配...

使用nginx+allure管理自动化测试报告

allure在自动化测试中经常用来生成漂亮的报告,但是网上及官网上给出的例子都仅仅是针对单个测试用例文件的形式介绍的,实际使用中,自动化测试往往需要包含不止一个产品或项目,本文介绍如何使用nginx+...

nginx配置文件详解(nginx配置文件详解高清版)

Nginx是一个强大的免费开源的HTTP服务器和反向代理服务器。在Web开发项目中,nginx常用作为静态文件服务器处理静态文件,并负责将动态请求转发至应用服务器(如Django,Flask,et...

SpringCloud Eureka-服务注册与发现

1.Eureka介绍1.1学习Eureka前的说明目前主流的服务注册&发现的组件是Nacos,但是Eureka作为老牌经典的服务注册&发现技术还是有必要学习一下,原因:(1)一些早期的分布式微服...

微服务 Spring Cloud 实战 Eureka+Gateway+Feign+Hystrix

前言我所在项目组刚接到一个微服务改造需求,技术选型为SpringCloud,具体需求是把部分项目使用SpringCloud技术进行重构。本篇文章中介绍了Eureka、Gateway、Fe...

深度剖析 Spring Cloud Eureka 底层实现原理

你作为一名互联网大厂后端技术开发人员,在构建分布式系统时,是不是常常为服务的注册与发现而头疼?你是否好奇,像SpringCloudEureka这样被广泛使用的组件,它的底层实现原理到底是怎样的...

热爱生活,喜欢折腾。(很热爱生活)

原文是stackoverflow的一则高票回答,原文链接可能之前也有人翻译过,但是刚好自己也有疑惑,所以搬运一下,个人水平有限所以可能翻译存在误差,欢迎指正(如侵删)。尽管classmethod和st...

GDB调试的高级技巧(详细描述gdb调试程序的全过程)

GDB是我们平时调试c/c++程序的利器,查起复杂的bug问题,比打印大法要好得多,但是也不得不说,gdb在默认情况下用起来并不是很好用,最近学习到几个高级点的技巧,分享下:一美化打印先上个例子...

Arduino 实例(二十三)Arduino 给Python 编译器发送信息

1首先Python需要安装Pyserial库,在命令提示符中输入pipintallpyserial若是遇到提示‘pip‘不是内部或外部命令,也不是可运行的程序或批处理文件,则需要设置环境变...