Rust 开发系列PyO3:Rust与Python的联动编程(上)

news2025/7/9 17:50:47

前言

Rust语言经常被人误认为是R语言,或者Ruby语言……但是做为近十年来tiobe最出人意料的编程语言,从冷门逐渐变成了明星,不过这次我们不讲Rust入门的内容,我们先来看看它一个很实用的功能——与Python的联动编程

在正式开始之前,可以先看以下几个问题:

  • 为什么要用Rust?
  • 答:Rust有很多优点,也有很多缺点,但是下面这个优点可以覆盖所有的缺点:即它能够编写出保证永远不出现内存错误的程序!!

 编程领域最头痛的问题,就是内存错误,这种错误无法排查,无法重现,随机出现,只能靠着程序员的编码能力,在编写代码的时候小心翼翼的检查以排除,所以C/C++虽然号称也可以写出内存安全的程序,但是实际上在大型程序中,几乎是一种可望不可及的理想状态。 而Rust以其特有的所有权设计思想,保证了在任何情况下的内存安全,只要编译通过,就永远不会出现内存错误。

  • 注意:不出现内存错误,不代表永远不会出现错误,而是出现任何错误,Rust都会告诉为什么会错,不会仅仅弹出一个不知所云的内存问题,让你无从下手,无从排查,也无法解决。

正文

全文一共4节,大约分为三章讲完,内容如下

  • 第一节:PyO3的简介,聊一下Python的联合开发方式,以及什么是PyO3
  • 第二节:从0开始编写一个PyO3程序,并且在Python中进行调用
  • 第三节:介绍PyO3提供的官方的脚手架,让我们如何快速的创建和编译Python扩展项目
  • 第四节:编写一系列计算密集型的插件,并且与Python原生代码和Python的JIT优化模式进行效率对比

第一节:Python扩展开发与PyO3简介

Python的优点,数不胜数,但是如果要说Python的缺点,那么下面这个应该是所有Python程序员最大的痛点:

Python有多慢呢? 下面这个是葡萄牙米尼奥大学和葡萄牙科英布拉大学联合的HAS Lab/INESC TEC(高性能计算实验室)在2020年11月发表的一篇论文,论文中对流行的编程语言进行了基准性能测试:

可以看见,无论是性能、时间、还是内存消耗,Python都几乎是倒数,特别是在计算性能和消耗时间上,几乎只有C、Rust、C++这三者的七十分之一!

当然,这里说个题外话,高性能有若干前提要求,不是说Python本身慢,写出来的东西就一定很慢,Python有大量优化方式和手段,例如静态编译(JIT),或者使用numpy这种高性能包,都能够大大提升Python的性能。

另外,程序运行的模式,也会对代码编写和架构有很大的影响,一般来说,在IO(数据)密集型运算上,管你用什么语言,性能都只能被数据吞吐这块短板所拖累……今天不谈这些内容,今天我们主要就针对计算(CPU)密集型运算这种与编程语言和编译器有密切关系的方式。

所以,抛开一切其他条件不谈,我们的诉求,就是一句话:

速度!
速度!!
还TM是速度!!

如果说Python原生的速度,就像体重和身高一样的正方体肥仔虾一样,从小学开始到大学毕业,体育考试中的长跑就从来没有及格过,基本上等于放弃治疗的话,如何最简单的提速呢?

请圣言

人和动物的区别在于,人能够创造工具,动物可以使用工具,但不能创造工具。

——卡尔 马克思

回过来,肥宅Py如何利用工具,变成狂飙Py呢?这就要从Python的扩展开发模式来看了:

首先,我们经常说的,Python,实际上指的是CPython,即C语言实现的Python,广义上,Python还有Jython和IronPython这两个版本,分别是Java的Python实现和.Net的Python实现,不过后面这两个东西,已经很久没有更新了,属于死了但是没有完全死的状态,基本上我们可以忽略……

所以,现在我们说的Python,事实上指的就是CPython,所以可以认为Python的底层就是C语言,那么官方提供的Python的扩展方式,就是用C语言来编写扩展模块(Python的标准库,绝大部分都是C语言开发的,C++属于蹭C语言的框架)。

其他语言如果扩展Python,只能通过如RPC(远程过程调用)这种方式,或者语言扩展桥接绑定的方式来实现,这两种模式就不能叫做扩展开发了。

那么Rust对于Python的扩展开发,又是哪一种方式呢? 答案是基于C语言的桥接模式。Rust可以直接继承C/C++的所有工具和库,也能够支持把自己编译成与C一样的动态链接库,所以我们可以把PyO3看成是Rust绑定C语言的一种扩展方式,也就是说,Rust PyO3写出来的扩展,与原生C写出来的扩展,对于Python是完全一样的。

PyO3开发出来的扩展模块,有如下特点:

  1. 开发出来的扩展模块,运行于Python同进程中,所以可以直接在Python语言环境中导入、调用、监控和管理。
  2. 可以在Rust中编写的、编译好的可执行程序中,调用和动态执行Python代码的能力,并且提供两种语言之间的控制与数据交互能力。

第二节:Python扩展开发与PyO3简介

首先还是用Rust的cargo包管理器去创建一个lib工程,这一步没啥说的。

如果你是一位从来没有接触过Rust的同学,那么可以先不必深究这些细节,建议直接看后面的结果。 然后就采用量子学习法即可:

  • 点赞就是看过了

  • 收藏就是学会了

  • 转发就是融会贯通了

然后在crates.io网站上,找到PyO3包,并且把它添加到配置文件里面去。当然,如果你很熟练了,也不用去网站上找,直接添加也行(建议去网站上,查找最新的版本号。)

crates.io是Rust的一个包管理仓库网站,类似于Python的pypi。

添加配置项,注意,需要有两个必选的配置项:

crate-type = ["cdylib"]

这个表示编译时候使用的c标准的动态库 Python的底层就是用c语言写的,必须是c标准库,Python才能导入

pyo3 = { version = "0.18.1", features = ["extension-module"] }

给工程添加最新的pyo3版本,并且设置特性为扩展模块,这个版本号一般我都选择最新的,所以在添加前都回去crates.io网站上查阅一下,不过你也可以通过

cargo add pyo3

这个命令来添加

之后,就可以编写Rust的功能实现代码了,主要需要编写两个部分: 

  1. 编写逻辑业务实现部分,我们这里实现一个say hello的功能,也就是用户输入一个用户名,这里输出一个问候,并且告诉他,这个问候来自Rust编写的后台扩展。

如果需要直接封装成Python可以调用的方法,需要在前面加#[pyfunction]这个属性,表示这个方法将对外暴露,并且被直接封装成Python可以调用的方法。

  1. 可以写一个测试方法,因为Rust是一种编译执行的语言,所以要执行一个方法必须要有main函数,如果不想添加main.rs和main函数的话,可以写一个测试方法,在cargo里面测试用。
  2. 把写好的Python扩展方法,封装到Python模块声明里面去,写一个Python模块声明方法。

#[pymodule]属性表示这个模块里面,要封装暴露哪些方法出来,而且还声明了是否有输入和输出参数。

全部写完之后,可以通过cargo test测试一下,方法是否能够正常执行,之后就可以打包封装了。

直接输入命令:

cargo build --release

对工程进行编译,在这个编译的过程中,cargo编译器会从网络上拉下一些依赖,然后在本地直接编译好,但是这个过程比较慢……特别是大型工程,一次编译可能需要几十分钟之久。

不过好在我们这个工程中没有什么需要依赖的大型库,所以编译还算快,在我本机上,11秒就编译好了。

之后,在工程的/target/release目录下面,会生成一个叫做pyo3demo.dll的动态链接库文件,这个就是windows平台下面的Python扩展模块,如果在Linux下面,会编程成so文件。

因为我们这里是一个裸奔的版本,所以后面还需要手动改一下名字,在Python中,扩展库的的后缀名都是pyd,所以需要我们手动把pyo3demo.dll改名为pyo3demo.pyd。

之后把这个pyd文件,拷贝到你的Python包索引环境的目录下面去,就可以了,最简单的就是直接拷贝到你py文件同目录下。

然后按照Python开发的一般模式,import这个包,然后直接使用即可:

如果输出的内容是我们测试中预想一样的,那就表示成功了。

最后,给出这个say hello应用中可能出现的一些场景问题:

打完收工。

最后,本工程的全部源码,在我gitee仓库如下位置:

博客: 博客文章,方便发文 - Gitee.com

注意: 我的代码里面还有后续的测试的内容,特别包含了polars包,所以编译起来比较慢,也特别大……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/396327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

易优cms searchform 搜索标签使用方法

searchform 搜索标签 【基础用法】 标签&#xff1a;searchform 描述&#xff1a;文档标题搜索&#xff0c;默认搜索整站 用法&#xff1a; {eyou:searchform typesonself} <form method"get" action"{$field.action}"> <input …

僵尸(Zombie)进程

文章目录1.僵尸进程2.产生僵尸进程的原因3.利用 wait 函数销毁僵尸进程4.使用 waitpid 函数销毁僵尸进程1.僵尸进程 进程完成工作后&#xff08;执行完 main 函数中的程序后&#xff09;应被销毁&#xff0c;但有时这些进程将变成僵尸进程&#xff0c;占用系统中的重要资源。这…

vue 监测数据改变的原理,添加属性

vue 监测数据改变的原理&#xff0c;添加属性 概况就是vue帮我们处理了&#xff0c;data的数据&#xff0c;加了get和set在生成虚拟dom模板之前&#xff0c; 开始做data数据的生成&#xff0c;get&#xff0c;set vue 提供的api : Vue.set(vm._data.student,‘key’,‘val’)…

C++复习笔记16

非类型的模板参数 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之后的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常量来使用。 注意&#xff1a; 1. 浮点数…

分享一个可以早点下班的开发小技巧

这次来跟大家分享一下让大家早下班的工具。首先要声明一下&#xff0c;分享的工作&#xff0c;是为了大部分人&#xff0c;而不是“某一个人”&#xff0c;不喜勿喷哈&#xff01; 介绍的就是这两年很火的低代码平台&#xff0c;网上两种观点&#xff1a; 第一种人是很不屑&am…

等保2.0与1.0 测评要求的变化

No.1标准内容增加了 标准内容上最大的变化就是将安全要求分为了安全通用要求和扩展要求。首先&#xff0c;安全通用要求部分已对1.0标准的内容进行了优化&#xff0c;删除或修订了过时的要求项&#xff0c;新增了对新型网络攻击行为防护和个人信息保护等方面的新要求。其次&am…

SAP 更改物料基本计量单位

前言部分 在SAP中物料创建后&#xff0c;一旦发生业务&#xff0c;其基本计量单位便很难修改。由于单位无法满足业务要求&#xff0c;往往会要求新建一个物料替代旧物料。这时候除了要将旧物料上所有的未清业务删除外&#xff0c;还需要替换工艺与BOM中的旧物料。特别是当出现旧…

一文带你看懂:亿级大表垂直拆分的工程实践

伴随着不断扩张的业务量&#xff0c;在数据库层面一般会经历数据拆分。解决问题的第一步&#xff0c;就是重新评估DB表结构设计的合理性。我们开发者会对表结构和业务代码进行重构&#xff0c;在之前的文章《业务系统重构》我有提到过。大表问题我实际遇到的是怎么样的情况呢&a…

CAD指令框找不到了怎么调出来?CAD指令框调出方法

CAD制图过程中&#xff0c;为了提高设计师的绘图效率&#xff0c;经常会用到各种CAD命令快捷键&#xff0c;可是CAD指令框突然不见了&#xff0c;这就让人很头疼了。CAD指令框找不到了怎么调出来呢&#xff1f;本节内容小编以浩辰CAD软件为例来给大家分享一下CAD指令框调出方法…

网络协议(十二):HTTPS(SSL/TLS、TLS1.2的连接)

网络协议系列文章 网络协议(一)&#xff1a;基本概念、计算机之间的连接方式 网络协议(二)&#xff1a;MAC地址、IP地址、子网掩码、子网和超网 网络协议(三)&#xff1a;路由器原理及数据包传输过程 网络协议(四)&#xff1a;网络分类、ISP、上网方式、公网私网、NAT 网络…

mysql 数据库 tinyint 类型字段取数变成 true/false 的解决方案

mysql 数据库 tinyint 类型字段取数变成 true/false 的解决方案 灌水 问题描述&#xff1a; 在 mysql 数据库设定上&#xff0c;有个字段类型是 tinyint 类型&#xff0c;长度为 1&#xff0c; 设定如下所示&#xff1a; 常规 sql 取数&#xff0c; 取到润乾报表内的时候&…

女神节告白代码

今天是女神节&#xff0c;送给所有女神们一句话&#xff1a; 爱自己是终生浪漫的开始&#xff0c;无论何时都要好好爱自己 目录 1. 请求动画帧填充 2.点类 3.粒子类 ​编辑 4.ParticlePool 池类 5.创建和填充 6.处理循环队列 7.更新活动粒子 8.移除非活性粒子 9.绘制有…

MQTT协议-CONNECT报文介绍

MQTT协议-CONNECT报文介绍 参考MQTT协议中文笔记&#xff1a;https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html Connect报文主要用于客户端连接服务器的&#xff0c;未涉及具体数据的传输&#xff0c;可以使用网络调试助手来连接阿里云平台&#xff…

什么是档案级光盘?它的寿命是多少年?

我们经常会听到有人在说&#xff1a;CD、DVD光盘的寿命多少多少年&#xff0c;蓝光光盘的寿命多少多少年。实际上这个说法是不对的&#xff0c;至少是不准确的&#xff0c;因为同样是CD、DVD光盘或者蓝光光盘&#xff0c;也分等级&#xff0c;而不同等级的光盘的寿命是不一样的…

ENVI_Classic:快速入门_菜单栏常见功能的基本介绍

说明&#xff1a;由于实验要求&#xff0c;所以并没有对各个功能进行详尽的解释&#xff0c;大多点到为止&#xff0c;少部分实验内容是实验要求所以步骤详尽。当然由于经验不足&#xff0c;有一些可能存在错误恳请指正.1. 实验目的通过ENVI Classic对自行下载的遥感图像进行一…

JavaScript Math 算数对象实例集合

文章目录JavaScript Math 算数对象实例集合使用 round() 对数字进行舍入使用 random() 来返回 0 到 1 之间的随机数使用 max() 来返回两个给定的数中的较大的数使用 min() 来返回两个给定的数中的较小的数摄氏度与华氏转换JavaScript Math 算数对象实例集合 注意&#xff1a; 了…

MySQL基础篇2

第一章 SQL语句之DQL 语法&#xff1a;查询不会对数据库中的数据进行修改&#xff0c;根据指定的方式来呈现数据。 语法格式&#xff1a; select * | 列名,列名 from 表名 [where 条件表达式] select 是查询指令&#xff0c;可以读 1 ~ n 行数据&#xff1b; 列名换成 * 号&a…

网络:TCP与UDP相关知识(详细)

目录&#xff1a;1、UDP 和 TCP 的特点与区别2、UDP 、TCP 首部格式3、TCP 的三次握手和四次挥手4、TCP 的三次握手&#xff08;为什么三次&#xff1f;&#xff09;5、TCP 的四次挥手&#xff08;为什么四次&#xff1f;&#xff09;6、TCP 长连接和短连接的区别7、TCP粘包、拆…

Caddy2学习笔记——Caddy2的安装、部署和编译小白教程

个人环境概述 本人拥有一个国内云服务商的云主机和一个备案好的域名&#xff0c;希望通过caddy2来作为web服务器。我的云主机是公网ip&#xff0c;地址为&#xff1a;43.126.100.78&#xff1b;我备案好的域名是&#xff1a;hotgirl.com。后面的文章都以上述的ip和域名来进行讲…

什么是jvm?

说明&#xff1a;做java开发的几乎都知道jvm这个名词&#xff0c;但是由于jvm对实际的简单开发的来说关联的还是不多&#xff0c;一般工作个一两年&#xff08;当然不包括爱学习的及专门做性能优化的什么的&#xff09;&#xff0c;很少有人能很好的去学习及理解什么是jvm&…