当前位置:首页 >热点 >结合使用 Python 和 Rust 可以使用 Python 进行原型设计

结合使用 Python 和 Rust 可以使用 Python 进行原型设计

2024-06-30 21:55:31 [百科] 来源:避面尹邢网

结合使用 Python 和 Rust

作者:Moshe Zadka 开发 Rust 和 Python 的结合优势互补。可以使用 Python 进行原型设计,使用然后将性能瓶颈转移到 Rust 上。结合

Python 和 Rust 是使用非常不同的语言,但它们实际上非常搭配。结合但在讨论如何将 Python 与 Rust 结合之前,使用我想先介绍一下 Rust 本身。结合你可能已经听说了这种语言,使用但可能还没有了解过它的结合细节。

结合使用 Python 和 Rust 可以使用 Python 进行原型设计

什么是使用 Rust?

Rust 是一种低级语言,这意味着程序员所处理的结合东西接近于计算机的 “真实” 运行方式。

结合使用 Python 和 Rust 可以使用 Python 进行原型设计

例如,使用整数类型由字节大小定义,结合与 CPU 支持的使用类型相对应。虽然我们很想简单地说 Rust 中的结合 a+b 对应于一条机器指令,但实际上并不完全是这样!

结合使用 Python 和 Rust 可以使用 Python 进行原型设计

Rust 编译器链非常复杂。作为第一种近似的方法,将这样的语句视为 “有点” 真实是有用的。

Rust 旨在实现零成本抽象,这意味着许多语言级别可用的抽象在运行时环境中会被编译去掉。

例如,除非明确要求,对象会在堆栈上分配。结果是,在 Rust 中创建本地对象没有运行时成本(尽管可能需要进行初始化)。

最后,Rust 是一种内存安全的语言。也有其他内存安全的语言和其他支持零成本抽象的语言。但通常这些是两类不同的语言。

内存安全并不意味着不可能在 Rust 中出现内存违规。它确实意味着只有两种方式可能导致内存违规:

  • 编译器的错误。
  • 显式声明为不安全(unsafe)的代码。

Rust 标准库代码有很多被标记为不安全的代码,虽然比许多人预期的少。这并不意味着该语句无意义。除了需要自己编写不安全代码的(罕见的)情况外,内存违规通常是由基础设施造成的。

为什么会有 Rust 出现?

为什么人们要创建 Rust?是哪些问题没有被现有编程语言解决吗?

Rust 被设计成既能高效运行,又保证内存安全。在现代的联网世界中,这是一个越来越重要的问题。

Rust 的典型应用场景是协议的低级解析。待解析的数据通常来自不受信任的来源,并且需要通过高效的方式进行解析。

如果你认为这听起来像 Web 浏览器所做的事情,那不是巧合。Rust 最初起源于 Mozilla 基金会,它是为了改进 Firefox 浏览器而设计的。

如今,需要保证安全和速度的不仅仅是浏览器。即使是常见的微服务架构也必须能够快速解析不受信任的数据,同时保证安全。

现实示例:统计字符

为了理解 “封装 Rust” 的例子,需要解决一个问题。这个问题需要满足以下要求:

  • 足够容易解决。
  • 能够写高性能循环来优化。
  • 有一定的现实意义。

这个玩具问题的例子是判断一个字符在一个字符串中是否出现超过了 X 次。这个问题不容易通过高效的正则表达式解决。即使是专门的 Numpy 代码也可能不够快,因为通常没有必要扫描整个字符串。

你可以想象一些 Python 库和技巧的组合来解决这个问题。然而,如果在低级别的语言中实现直接的算法,它会非常快,并且更易于阅读。

为了使问题稍微有趣一些,以演示 Rust 的一些有趣部分,这个问题增加了一些变化。该算法支持在换行符处重置计数(意即:字符是否在一行中出现了超过 X 次?)或在空格处重置计数(意即:字符是否在单词中出现了超过 X 次?)。

这是唯一与 “现实性” 相关的部分。过多的现实性将使这个示例在教育上不再有用。

支持枚举

Rust 支持使用枚举(enum)。你可以使用枚举做很多有趣的事情。

目前,只使用了一个简单的三选一的枚举,并没有其他的变形。这个枚举编码了哪种字符重置计数。

#[derive(Copy)]enum Reset {     NewlinesReset,    SpacesReset,    NoReset,}

支持结构

接下来的 Rust 组件更大一些:这是一个结构(struct)。Rust 的结构与 Python 的 dataclass 有些相似。同样,你可以用结构做更复杂的事情。

#[pyclass]struct Counter {     what: char,    min_number: u64,    reset: Reset, }

实现块

你可以在 Rust 中使用一个单独的块,称为实现(impl)块,为结构添加一个方法。但具体细节超出了本文的范围。

在这个示例中,该方法调用了一个外部函数。这主要是为了分解代码。更复杂的用例将指示 Rust 编译器内联该函数,以便在不产生任何运行时成本的情况下提高可读性。

#[pymethods]impl Counter {     #[new]    fn new(what: char, min_number: u64, reset: Reset) -> Self {         Counter{ what: what, min_number: min_number, reset: reset}    }    fn has_count(        &self,        data: &str,    ) -> bool {         has_count(self, data.chars())    }}

函数

默认情况下,Rust 变量是常量。由于当前的计数(current_count)必须更改,因此它被声明为可变变量。

fn has_count(cntr: &Counter, chars: std::str::Chars) -> bool {     let mut current_count : u64 = 0;    for c in chars {         if got_count(cntr, c, &mut current_count) {             return true;        }    }    false}

该循环遍历字符并调用 got_count 函数。再次强调,这是为了将代码分解成幻灯片展示。它展示了如何向函数发送可变引用。

尽管 current_count 是可变的,但发送和接收站点都显式标记该引用为可变。这可以清楚地表明哪些函数可能修改一个值。

计数

got_count 函数重置计数器,将其递增,然后检查它。Rust 的冒号分隔的表达式序列评估最后一个表达式的结果,即是否达到了指定的阈值。

fn got_count(cntr: &Counter, c: char, current_count: &mut u64) -> bool {     maybe_reset(cntr, c, current_count);    maybe_incr(cntr, c, current_count);    *current_count >= cntr.min_number}

重置代码

reset 的代码展示了 Rust 中另一个有用的功能:模式匹配。对 Rust 中匹配的完整描述需要一个学期级别的课程,不适合在一个无关的演讲中讲解。这个示例匹配了该元组的两个选项之一。

fn maybe_reset(cntr: &Counter, c: char, current_count: &mut u64) -> () {     match (c, cntr.reset) {         ('\n', Reset::NewlinesReset) | (' ', Reset::SpacesReset)=> {             *current_count = 0;        }        _ => { }    };}

增量支持

增量将字符与所需字符进行比较,并在匹配时增加计数。

fn maybe_incr(cntr: &Counter, c: char, current_count: &mut u64) -> (){     if c == cntr.what {         *current_count += 1;    };}

请注意,我在本文中优化了代码以适合幻灯片。这不一定是 Rust 代码的最佳实践示例,也不是如何设计良好的 API 的示例。

为 Python 封装 Rust 代码

为了将 Rust 代码封装到 Python 中,你可以使用 PyO3。PyO3 Rust “crate”(即库)允许内联提示将 Rust 代码包装为 Python,使得修改两者更容易。

包含 PyO3 crate 原语

首先,你必须包含 PyO3 crate 原语。

use pyo3::prelude::*;

封装枚举

枚举需要被封装。derive 从句对于将枚举封装为 PyO3 是必需的,因为它们允许类被复制和克隆,使它们更容易在 Python 中使用。

#[pyclass]#[derive(Clone)]#[derive(Copy)]enum Reset {     /* ... */}

封装结构

结构同样需要被封装。在 Rust 中,这些被称为 “宏”,它们会生成所需的接口位。

#[pyclass]struct Counter {     /* ... */}

封装实现

封装实现(impl)更有趣。增加了另一个名为 new 的宏。此方法被标记为 #[new],让 PyO3 知道如何为内置对象公开构造函数。

#[pymethods]impl Counter {     #[new]    fn new(what: char, min_number: u64,          reset: Reset) -> Self {         Counter{ what: what,          min_number: min_number, reset: reset}    }    /* ... */}

定义模块

最后,定义一个初始化模块的函数。此函数具有特定的签名,必须与模块同名,并用 #[pymodule] 修饰。

#[pymodule]fn counter(_py: Python, m: &PyModule) -> PyResult<()> {     m.add_class::<Counter>()?;    m.add_class::<Reset>()?;    Ok(())}

? 显示此函数可能失败(例如,如果类没有正确配置)。 PyResult 在导入时转换为 Python 异常。

Maturin 开发

为了快速检查,用 maturin develop 构建并将库安装到当前虚拟环境中。这有助于快速迭代。

$ maturin develop

Maturin 构建

maturin build 命令构建一个 manylinux 轮子,它可以上传到 PyPI。轮子是特定于 CPU 架构的。

Python 库

从 Python 中使用库是最简单的部分。没有任何东西表明这与在 Python 中编写代码有什么区别。这其中的一个有用方面是,如果你优化了已经有单元测试的 Python 中的现有库,你可以使用 Python 单元测试来测试 Rust 库。

导入

无论你是使用 maturin develop 还是 pip install 来安装它,导入库都是使用 import 完成的。

import counter

构造函数

构造函数的定义正好使对象可以从 Python 构建。这并不总是如此。有时仅从更复杂的函数返回对象。

cntr = counter.Counter(    'c',    3,    counter.Reset.NewlinesReset,)

调用函数

最终的收益终于来了。检查这个字符串是否至少有三个 “c” 字符:

>>> cntr.has_count("hello-c-c-c-goodbye")True

添加一个换行符会触发剩余操作,这里没有插入换行符的三个 “c” 字符:

>>> cntr.has_count("hello-c-c-\nc-goodbye")False

使用 Rust 和 Python 很容易

我的目标是让你相信将 Rust 和 Python 结合起来很简单。我编写了一些“粘合剂”代码。Rust 和 Python 具有互补的优点和缺点。

Rust 非常适合高性能、安全的代码。Rust 具有陡峭的学习曲线,对于快速原型解决方案而言可能有些笨拙。

Python 很容易入手,并支持非常紧密的迭代循环。Python 确实有一个“速度上限”。超过一定程度后,从 Python 中获得更好的性能就更难了。

将它们结合起来完美无缝。在 Python 中进行原型设计,并将性能瓶颈移至 Rust 中。

使用 Maturin,你的开发和部署流程更容易进行。开发、构建并享受这一组合吧!

责任编辑:庞桂玉 来源: Linux中国 PythonRust

(责任编辑:热点)

    推荐文章
    • 支付宝基金的钱可以随时取出吗 到账时间是多久?

      支付宝基金的钱可以随时取出吗 到账时间是多久?支付宝是第三方支付平台,除了支付之外,还可以购买理财产品,比如基金。最近有网友询问,支付宝基金的钱可以随时取吗?一般多久可以到账?想要知道答案的朋友,跟小编一起去看看吧。据了解,支付宝基金的钱是可以随 ...[详细]
    • 华为nova新机曝光 全系或将更换联发科处理器

      华为nova新机曝光 全系或将更换联发科处理器华为nova7系列已经发布有段日子了,作为华为系产品里主打年轻受众群体的产品线,也快到了它更新的日子,所以关于下一代nova8系列的曝光也就出现了。近日,微博认证为数码博主的用户带来了华为nova8系 ...[详细]
    • 智慧停车平台ETCP获15.5亿B轮融资额

      智慧停车平台ETCP获15.5亿B轮融资额近日,互联网+智慧停车行业再曝重量级消息。国内智慧停车创始企业ETCP宣布完成B轮巨额融资,融资额度为15.5亿元。据了解,这是继ETCP在国内收获行业最高的A轮融资后,整个智慧停车行业内再次曝出的最 ...[详细]
    • OPPO官网新机上架 1199元起售

      OPPO官网新机上架 1199元起售9月11日消息,今天OPPO悄无声息的在其官网更新了一款A系列的新机OPPO A32。这款手机搭载了一块6.5英寸的LCD单挖孔屏幕,分辨率为1600 x 720,最高支持90Hz的刷新率后置三摄镜头 ...[详细]
    • 6G概念板块下挫 信维通信(300136CN)跌1.76%

      6G概念板块下挫  信维通信(300136CN)跌1.76%今日早盘,截至11:00,6G概念板块下挫。*ST华讯(000687CN)跌4.86%报1.76元,亚光科技(300123CN)跌2.02%报9.22元,信维通信(300136CN)跌1.76%报27 ...[详细]
    • 新奇圆珠笔还能应急当充电宝

      新奇圆珠笔还能应急当充电宝现在很多物品在朝着一物多用的方向发展,也就是说集多种功能于一身。今天我们就来围观一款国外正在流行了多用圆珠笔,不仅具有圆珠笔的功能,竟然还隐藏的充电宝的功能,实用性很高。话不多说,我们一起来围观一下吧 ...[详细]
    • 炽热的互联网医疗 2017还会热闹下去吗?

      炽热的互联网医疗 2017还会热闹下去吗?从1994年创业至今,九安医疗经历了传统硬件生产、贴牌生意,现在转型搭建慢病管理健康生态系统。其董事长刘毅转型互联网医疗的决心十分坚定,他认为互联网医疗是对传统医疗行业的提升和改变,这些改变不仅仅是医 ...[详细]
    • 美国禁令正式生效!多家供应商断供华为:倪光南院士回应

      美国禁令正式生效!多家供应商断供华为:倪光南院士回应今天9月15日),美国对华为的新禁令正式生效。已知消息显示,台积电、高通、三星、SK海力士、美光等主要元器件厂商将不再供应芯片给华为。追溯其根源,是美国针对华为做出的一系列禁令,即所有使用美国技术的公 ...[详细]
    • 华阳股份(600348.SH)公布消息:拟开展应收账款保理业务

      华阳股份(600348.SH)公布消息:拟开展应收账款保理业务华阳股份(600348.SH)公布,公司及公司下属煤炭销售公司拟将一定期间内向华能国际电力股份有限公司(“华能国际”)电厂供应煤炭所形成的应收账款用于办理应收账款保理融资业务。 ...[详细]
    • 暴雪中国:寻找机遇将游戏重新带回给大家!

      暴雪中国:寻找机遇将游戏重新带回给大家!暴雪中国官方微博今日发文,称正寻找机遇将我们的游戏于未来重新带回给大家。微博原文如下:这两天来,我们看到了大家真挚叙述的,与暴雪游戏相关的珍贵回忆与往事点滴。我们很感激大家,在过往近20年来向我们所传 ...[详细]
    热点阅读