用Rust为Python编写软件包,PyO3与Maturin的快速使用

语言杂谈

Rust

在这里插入图片描述
Rust社区最近一直很火热,本人也赶着时髦,浅浅的学了一下Rust。不得不说语言的设计很新颖,解决了很多痛点,但同样也有不少问题,老C++人感觉最明显的就是写Rust需要考虑各种规则,编译器时刻提醒我是傻x让我很难受,不过我还是很看好Rust的,以后会是我的主力语言之一,希望这门语言能够越来越好(加入Rust邪教,重写一切吧)。

Python

在这里插入图片描述
在很久以前,我一直对Python保持偏见,老C++人坚持手写一切,没使用Python之前,一听说调包就能完成各种功能,我对此十分鄙夷,这叫写代码?不知道底层,只要调包,这也太虚无了(语言没有高低之分,只有领域不同,原谅我年轻时的短见与无知)。等后面真的开始使用了,真香!太香了!人生苦短,我用Python!如果世界上只需要一门语言,那一定就是Python!从开始用Python之后,Lua、Perl、Bat、Shell这些脚本我就慢慢的不怎么用了,也算是另一种NTR了。

正片教程

Rust和Python一样有着丰富的包,Python的很多包底层都是C/C++编写的,而Rust天生就兼容C,那么可不可以用Rust为Python编写软件包,实现Python调用Rust?与原始的Python代码比起来,使用Rust代码具有更好的性能和速度。
显然我们能想到的,大佬们早早就想到了,PyO3就可以实现这个目标。PyO3 不仅仅提供了rust binding,也提供了创建Python包的开箱即用的脚手架工具maturin,使用maturin我们可以很方便地创建一个基于Rust开发的Python扩展模块。

实验环境

  • Python3.10
  • Windos 11
  • Rust 1.61

安装maturin&&创建项目

pip3 install maturin
maturin new rust_mod #项目名称默认会是Python的包名

选择pyo3,完成创建
在这里插入图片描述

编写&&编译Rust模块

接下来就是的工作就是写编写Rust代码,打开lib.rs文件,默认会有一个模板,我们修改模板就行了。
#[pyfunction]宏是定义生成的Python包里的函数。
#[pymodule]宏是创建Python的包实现,在包里添加各种包内成员。
除了以上的这些还有几种别的宏,用于添加类,方法等等,详细参考PyO3文档,我们演示就用最简单的,创建几个函数,功能是都是实现二维码生成,但保存格式不同。

  1. Crates.io中查找qrcode包,在Cargo.toml中导入相关包。
[dependencies]
pyo3 = { version = "0.17.1", features = ["extension-module"] } //PyO3依赖,默认创建就有
qrcode = "0.12.0"
image = "0.23.14"
  1. 编写代码,可直接参考qrcodecrates.io中的示例代码。
use image::Luma;
use pyo3::prelude::*;
use qrcode::render::unicode;

/// 二维码图片生成函数
#[pyfunction]
fn qrcode_img(data: String, save_path: String) {
    // Encode some data into bits.
    let code = QrCode::new(data.as_bytes()).unwrap();
    // Render the bits into an image.
    let image = code.render::<Luma<u8>>().build();
    // Save the image.
    image.save(save_path).unwrap();
}

/// 二维码字符生成函数
#[pyfunction]
fn qrcode_str(data: String) {
    let code = QrCode::new(data.as_bytes()).unwrap();
    let string = code
        .render::<char>()
        .quiet_zone(false)
        .module_dimensions(2, 1)
        .build();
    println!("{}", string);
}

/// 二维码unicode生成函数
#[pyfunction]
fn qrcode_unicode(data: String) {
    let code = QrCode::new(data.as_bytes()).unwrap();
    let image = code
        .render::<unicode::Dense1x2>()
        .dark_color(unicode::Dense1x2::Light)
        .light_color(unicode::Dense1x2::Dark)
        .build();
    println!("{}", image);
}

/// A Python module implemented in Rust.
#[pymodule]
fn rust_mod(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(qrcode_img, m)?)?;
    m.add_function(wrap_pyfunction!(qrcode_str, m)?)?;
    m.add_function(wrap_pyfunction!(qrcode_unicode, m)?)?;
    Ok(())
}
  1. 编译生成软件包。
cd rust_mod
maturin build -f -r

在这里插入图片描述
完成编译生成whl文件:
在这里插入图片描述
注意:这里编译过程,-f不要去掉了,在windows平台测试,没有-f会报错,提示解释器路径无效,我反复测试了,应该是个Bug,不过好像并没有修复,有人在github上问过,但是没有解答
错误提示:
在这里插入图片描述

安装包&&Python调用

  1. 使用pip安装包。
cd .\target\wheels\
pip3 install .\rust_mod-0.1.0-cp310-none-win_amd64.whl

在这里插入图片描述
2. Python调用代码。

import rust_mod

if __name__ == '__main__':
    # 生成二维码图片保存
    rust_mod.qrcode_img("hello","./qrcode.png")
    # 生成二维码字符串
    rust_mod.qrcode_str("hello")
    print("\n")
    # 生成二维码unicode
    rust_mod.qrcode_unicode("hello")

运行结果:
在这里插入图片描述
在文件夹中也生成了相应的qrcode.png

性能对比

使用python的第三方库qrcode,进行性能对比。

  1. 安装qrcode
pip install qrcode
  1. Python性能测试代码。
import rust_mod
import qrcode
import time



if __name__ == '__main__':
    t0 = time.time()
    # rust包调用
    rust_mod.qrcode_img("hello", "./qrcode1.png")
    t1 = time.time()
    # python包调用
    img = qrcode.make("hello")
    img.save("./qrcode2.png")
    t2 = time.time()

    print("Rust time: %fs" % (t1-t0))
    print("Python time: %fs" % (t2-t1))

运行结果:
在这里插入图片描述
这个性能差距还是挺大的,差了一个量级,不过这样对比不太公平,毕竟底层细节我们也不知道,没法控制变量,但是有一点可以知道,大多数时候Rust编写的包性能还是够的。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
可以通过使用`pyo3`库来将`Python`类方法导出到`Rust`,然后在`Rust`中调用这些方法。以下是一个例子: ```python # example.py class MyClass: def __init__(self, name): self.name = name def greet(self): print(f"Hello, {self.name}!") ``` ```rust // main.rs use pyo3::prelude::*; use pyo3::wrap_pyfunction; #[pyclass] struct MyClass { name: String, } #[pymethods] impl MyClass { #[new] fn new(name: String) -> Self { Self { name } } fn greet(&self) -> PyResult<()> { Python::with_gil(|py| { let gil = pyo3::Python::acquire_gil(); let py = gil.python(); let locals = [("self", pyo3::PyObject::from(self))].into_py_dict(py); py.run("self.greet()", None, Some(&locals))?; Ok(()) }) } } #[pyfunction] fn create_my_class(name: String) -> PyResult<MyClass> { Ok(MyClass::new(name)) } #[pymodule] fn example(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::<MyClass>()?; m.add_wrapped(wrap_pyfunction!(create_my_class))?; Ok(()) } fn main() -> PyResult<()> { Python::with_gil(|py| { let example_module = PyModule::new(py, "example")?; example_module.add_class::<MyClass>()?; example_module.add_wrapped(wrap_pyfunction!(create_my_class))?; let locals = [("example", example_module)].into_py_dict(py); py.run("import example", None, Some(&locals))?; let my_class = example_module .call("create_my_class", ("World",), None)? .extract::<MyClass>()?; my_class.greet()?; Ok(()) }) } ``` 这个例子中,我们使用`pyo3`库将`Python`类`MyClass`导出到`Rust`中。在`Rust`代码中,我们可以通过调用`MyClass`的`greet`方法来执行`Python`类中的`greet`方法。我们还编写了一个`Rust`函数`create_my_class`,用于创建`MyClass`实例并将其返回到`Python`。最后,我们在`Rust`中调用`Python`代码来创建`MyClass`实例,然后调用`greet`方法来打印出“Hello, World!”的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值