Py学习  »  Python

在 Python 里用 Rust:让 Python 再次强大!

大迁世界 • 2 天前 • 10 次点击  

我有一支技术全面、经验丰富的小型团队,专注高效交付中等规模外包项目,有需要外包项目的可以联系我

Python 与 Rust 风格截然不同,但组合起来却意外合拍。在讨论如何把二者拼在一起之前,先快速认识一下 Rust。你大概听过它的名号,但未必摸清了它的“脾气”。

什么是 Rust?

Rust 属于低层语言,程序员需要更贴近机器的真实工作方式来思考。

举例:整数类型按 字节位宽区分,对应 CPU 支持的类型。直觉上你或许会说:在 Rust 里 a + b 就是“一条机器指令”。但编译链太复杂,这种说法只在粗略意义上成立

Rust 的目标是零成本抽象:许多高级抽象在运行期会被编译器“抹平”,不额外付费。

例如:对象默认分配在上(除非你显式要求堆分配),因此创建原生对象没有运行期开销(尽管初始化仍然需要)。

最后,Rust 是内存安全语言。别的语言也可能提供内存安全或零成本抽象,但往往不是同一类。 内存安全并不代表“永不违规”,而是仅有两种途径会出事:

  1. 编译器报错;
  2. 你显式写了 unsafe 的代码。

标准库里确实有一些 unsafe,但远少于多数人的想象。这并不削弱前述结论:除非你(少数场景里)必须手写 unsafe大多数违规来自底层基础设施而非业务面。

为什么要用 Rust?

Rust 诞生是为同时追求 效率内存安全。在互联环境里,这个目标越来越关键。

典型场景:底层协议解析。输入常来自不可信源,既要快,又要稳。

这听起来像浏览器在做的事?没错。Rust 来自 Mozilla 基金会,最初就是为了改进 Firefox。

如今不止浏览器:常见的微服务同样要快速解析不可信数据并且保证安全。

示例:统计字符

为理解“把 Rust 包一层给 Python 用”的例子,我们先设定一个问题,满足:

  • 足够简单;
  • 能写出高性能循环;
  • 有点实际价值。

具体问题:判断某字符在字符串里是否出现超过 X 次。这个需求用“高效正则”未必好写;即便用 Numpy 等技巧,也常需要整串扫描,而直觉算法在低层语言里会更快更易读

为了展示 Rust 的一些点,我们再加两种“重置计数”的变体:

  • 遇到换行重置(即“某行内是否超过 X 次?”)
  • 遇到空白重置(即“某个单词内是否超过 X 次?”)

枚举(enum)

Rust 的枚举很强大。这里用一个“三选一”的简单枚举,表示 何时重置计数

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

结构体(struct)

结构体有点像 Python 的 dataclass,但能做的更复杂。

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

实现块(impl)

通过 impl 给结构体加方法。本例里方法再调用外部函数,方便拆分逻辑;复杂场景下编译器会内联,提升可读性同时不增加运行成本。

#[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 需要变化,所以要用 mut

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。这也演示了可变引用的传递:调用方与被调方都要显式标注可变,修改意图更清晰

计数逻辑

重置 → 自增 → 比较阈值。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
}

重置

这里用到了模式匹配。完整讲解可以开一门课——本例只匹配元组的若干情形:

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;
    };
}

注:为讲解直观,本文代码偏教学取向,并非最佳实践或完美 API 设计范式。

把 Rust 包给 Python 用

可以使用 PyO3。这个 Rust crate 通过注解把 Rust 类型/方法暴露为 Python 扩展,让两端更易同时迭代。

引用 PyO3




    
use pyo3::prelude::*;

包装枚举

派生 Clone/Copy 便于在 Python 侧使用与传递。

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

包装结构体

用 #[pyclass] 生成必要接口。

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

包装实现(构造器)

#[pymethods] +  #[new] 指定 Python 侧的构造方法。

#[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::()?;
    m.add_class::()?;
    Ok(())
}

? 表示可能失败(如类未正确注册);PyResult 会在导入时转换成 Python 异常。


用 maturin 开发/构建

快速迭代:把编译后的扩展直接装到当前虚拟环境。

$ maturin develop

产出分发包

$ maturin build

会生成 manylinux 的 wheel(按 CPU 架构区分),可上传到 PyPI。

在 Python 里使用

这一部分最“丝滑”:用法几乎与纯 Python 库别无二致。这也意味着:如果你在优化既有 Python 库,只要接口不变,原有单测就能直接覆盖到 Rust 实现。

导入

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,其实很容易

Press enter or click to view image in full size

本文的目的,是让你相信把 Rust 与 Python 结合并不难。 Rust 负责 高性能与安全,但上手曲线更陡;Python 负责极快迭代,但存在性能上限

因此:原型用 Python,瓶颈用 Rust。 有了 maturin,开发与发布都更顺畅:写 → 构建 → 享受组合拳


全栈AI·探索:涵盖动效、React Hooks、Vue 技巧、LLM 应用、Python 脚本等专栏,案例驱动实战学习,点击二维码了解更多详情。

图片
最后:

20个前端开发者必备的响应式布局

深入React:从基础到最佳实践完整攻略
python 技巧精讲
React Hook 深入浅出
CSS技巧与案例详解
vue2与vue3技巧合集

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/187248
 
10 次点击