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

【Rust中级教程】题外话:Rust + Python联合编程(基于Maturin)

1. 为啥要利用Rust加速Python?

我相信大部分都知道Rust和Python这两个语言的利弊:

  • Rust性能好,但是代码难写
  • Python代码好写,但是性能不好

那为什么不把两者结合起来呢?让Python在顶层调用,用Rust写性能敏感的模块。

2. 需要的软件及环境

我这里使用的是JetBrains的RustRover,目前是免费的。有的人会觉得它不好用,用VScode也是可以的。

打开RustRover,由于我们需要写Python的代码块,所以我们得先安装Python插件。在插件里搜索Python Community Edition安装即可。
请添加图片描述

注:安装好后需要根据IDE的提示重启IDE这个插件才会生效。

3. 基础教程

我们创建一个项目,由于RustRover会在创建项目之后会自动配置Cargo,生成Cargo那一堆东西。所以我们得先把里面的所有东西全删掉。
请添加图片描述

由于我们是利用Rust加速Python,所以我们得把整个项目当做一个Python项目而不是Rust项目来做。在终端中(可以点击IDE左下角的终端标志来在IDE中输入终端命令),使用下面的代码创建一个Python虚拟环境:

python -m venv .venv
  • .venv是我起的名字,你可以替换

创建好后的项目应该长这样:
请添加图片描述

接下来我们需要激活这个虚拟环境,在终端使用如下指令:
For MacOS/Linux:

source .venv/bin/activate

For Windows:

.\.venv\Scripts\avtivate

执行之后如果你的命令行前缀多了(.venv).venv是我起的名字,如果你换用其它名字就会不一样),就是没问题的。

注意:如果你的环境用了conda得先退掉(你的命令行前缀有(base)标记),因为maturin不允许环境变量中同时存在Python venvConda的激活环境,导致冲突。输入以下指令以退出conda环境:

conda deactivate

maturin是python的一个包,我们得在这个虚拟环境中先安装:

pip install maturin

下载好之后就可以把这个项目整成Rust和Python结合的样子了。在终端输入指令:

maturin init

这时候它会给你3个选项,你选择第一个按回车即可:
请添加图片描述

这时候注意看项目文件结构,就既有Python也有Rust了:
请添加图片描述

  • Rust文件放在src目录下
  • Python文件可以在项目的顶层目录下创建

我们先创建main.py这个Python文件:
请添加图片描述

打开src文件夹,看看lib.rs,它里面已经有代码了:

use pyo3::prelude::*;  
  
/// Formats the sum of two numbers as string.  
#[pyfunction]  
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {  
    Ok((a + b).to_string())  
}  
  
/// A Python module implemented in Rust.  
#[pymodule]  
fn maturin_test(m: &Bound<'_, PyModule>) -> PyResult<()> {  
    m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;  
    Ok(())  
}
  • 写在#[pyfunction]标注下的函数(比如sum_as_string)是你想要给Python用的函数,但是Python不能直接看到,得先把这个给#[pymodule]标注下的函数(比如maturin_test),让它调用一遍,这样Python就会把sum_as_string视作为maturin_test这个库下的一个函数
  • #[pymodule]标注下的函数会在Python中显示为一个库

但目前这样Python还看不见,我们得先让maturin给我们编译Rust代码供Python代码调用。在终端输入:

maturin develop

之后maturin就会开始编译你的Rust代码成.dll文件,然后改为.pyd文件(MacOS/Linus上会生成.so文件)

编译完之后你看.venv/bin目录下多了一个maturin_test文件夹,这个文件夹会被识别为一个包供Python代码使用。

再写Python代码之前还有最后一步:配置解释器。打开设置,搜索“Python解释器”,点击“添加解释器”,接着点击“添加本地解释器”,选择“选择现有”,添加这个项目路径下的的Python解释器即可。
请添加图片描述

大功告成,我们来随便写点验证一下吧:

import maturin_test  
  
num = maturin_test.sum_as_string(1, 1)  
print(num)

输出:

2

没有问题,这正是我们想要的结果

如果你的代码在这一步报错,那大概率是需要你手动编辑Python运行设置。点击右上角"Run"右边的三角尖,选择“编辑配置”
请添加图片描述

打开之后会进入“运行/调试配置”窗口,这时候点击左上角的加号,添加Python配置,它的配置可以直接抄图中的:
请添加图片描述

点击“确定”,然后退出这个界面,再点击运行即可。

4. 添加存根(可选)

如果你仔细看刚才写代码,会发现有一处波浪线警告:
请添加图片描述

警告信息是找不到’sum_as_string’函数。这是因为.pyd(在MacOS/Linux上是.so)文件里面存的是二进制源码,Python解释器不可能从中找出具体有哪些函数在这个库里,所以就报警。

这并不影响实际写代码。但是如果你跟我一样是一位强迫症患者,就可以通过添加存根的方式来解决问题。Python 的 存根(Stub) 是用于类型提示.pyi文件,它只包含函数、类和变量的声明(不包含实现),用于静态类型检查工具来提供类型信息。

在项目的顶层目录下添加一个Python存根文件即可,名称与maturin_test这个库的名字保持一致:
请添加图片描述

在里面这么写:

def sum_as_string(a: int, b: int) -> str: ...

这时候就不会有报警了。如果你以后还要在Rust代码里再加函数并在Python代码中运用,就到存根文件里写个函数头即可。

5. 实战(例子)

询问用户输入 n,然后计算第 n 个斐波那契数。

纯Python的实现:

def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

if __name__ == "__main__":
    n = int(input("请输入要计算的斐波那契数列位置 n: "))
    result = fibonacci(n)
    print(f"Fibonacci({n}) = {result}")

纯Rust的实现:

use std::io;

fn fibonacci(n: usize) -> usize {
    let mut a = 0;
    let mut b = 1;
    for _ in 0..n {
        let temp = b;
        b = a + b;
        a = temp;
    }
    a
}

fn main() {
    println!("请输入要计算的斐波那契数列位置 n:");

    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("读取输入失败");
    
    let n: usize = input.trim().parse().expect("请输入一个有效的正整数");

    let result = fibonacci(n);
    println!("Fibonacci({}) = {}", n, result);
}

可以看到,计算斐波那契数列Rust代码性能占优,但是Rust询问用户输入的部分又过于繁琐;Python在这两点上正好相反。我们就可以依靠maturin各取所长。

前面的配置部分我不再重复了,我们从写lib.rs开始:

use pyo3::prelude::*;  
  
#[pyfunction]  
fn fibonacci(n: usize) -> usize {  
    let mut a = 0;  
    let mut b = 1;  
    for _ in 0..n {  
        let temp = b;  
        b = a + b;  
        a = temp;  
    }  
    a  
}  
  
#[pymodule]  
fn maturin_test(m: &Bound<'_, PyModule>) -> PyResult<()> {  
    m.add_function(wrap_pyfunction!(fibonacci, m)?)?;  
    Ok(())  
}
  • 直接把Rust的斐波那契数列函数粘过来即可,放在#[pyfunction]标注下
  • #[pymodule]下写了函数maturin_test,maturin_test又调用了fibonacci这个函数。这样Python代码就会把maturin_test视作为一个包,而fibonacci会被视作这个包下的一个函数

编译一下Rust代码,输入指令:

maturin develop

在存根文件maturin_test.pyi中写上函数的函数头:

def fibonacci(n: int) -> int: ...

main.py下这么写:

import maturin_test  
  
if __name__ == "__main__":  
    n = int(input("请输入要计算的斐波那契数列位置 n: "))  
    result = maturin_test.fibonacci(n)  
    print(f"Fibonacci({n}) = {result}")

输出:

请输入要计算的斐波那契数列位置 n: 40
Fibonacci(40) = 102334155

http://www.kler.cn/a/566698.html

相关文章:

  • 城电科技|会追日的智能花,光伏太阳花开启绿色能源新篇章
  • 一个便捷的web截图库~
  • 回溯算法(C/C++)
  • mysql中事务的基本概念
  • 开源PDF解析工具olmOCR
  • next.js-学习3
  • USRP4120-通用软件无线电平台
  • C语言一维数组的全面解析
  • CAN总线通信协议学习1——物理层
  • HarmonyOS 5.0应用开发——多线程Worker和@Sendable的使用方法
  • 版图自动化连接算法开发 00004 ------ 给定一个点,添加一个中间点实现 Manhattan 方式连接两个给定的坐标点
  • I2S音频开发(使用USB音频进行验证)
  • ESP32 S3开发笔记(环境搭建,成功烧录)
  • Windows逆向工程入门之MASM整数存储机制
  • Grok3使用体验与模型版本对比分析
  • Haption:机器人遥操作触觉力反馈技术革新解决方案
  • 工程化与框架系列(10)--微前端架构
  • 量子计算如何优化交通流量:未来智能出行的钥匙
  • 欢乐力扣:存在重复元素二
  • go基础语法