
1. 引言消除隔阂一步调用在上一篇中我们分别用 Python 和 Rust 写了两个独立的 HTTP 服务器。虽然对比了性能但它们本质上是两个独立的程序。在实际开发中我们往往不想维护两套服务。我们希望的结果是Python 负责主流程和业务逻辑而在性能瓶颈处直接调用 Rust 编写的函数就像调用普通的 Python 库一样自然。这就需要一个桥梁。PyO3 就是这个桥梁它允许你将 Rust 代码编译成一个.soLinux/Mac或.pydWindows的二进制模块。Python 通过import加载它中间的转换开销极小。今天我们就把上一篇中的count_words函数打包成一个名为rust_analyzer的 Python 扩展库。2. 环境准备Maturin 工具链为了编译 Rust 生成的 Python 模块我们需要特定的工具。maturin是 PyO3 官方推荐的工具它极大地简化了配置过程。2.1 创建虚拟环境为了保持环境整洁我们先创建一个 Python 虚拟环境# 创建项目文件夹 mkdir pyo3_demo cd pyo3_demo # 创建虚拟环境 python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate2.2 安装 Maturinpip install maturin3. 初始化 PyO3 项目maturin提供了一个命令可以快速生成符合 PyO3 规范的项目骨架。# 在当前目录下初始化一个名为 rust_analyzer 的库 maturin init --bindings pyo3运行后它会询问你选择哪种绑定方式直接回车选择默认的pyo3即可。生成的项目结构如下pyo3_demo/ ├── Cargo.toml # Rust 项目管理文件 ├── pyproject.toml # Python 构建配置文件 └── src/ └── lib.rs # Rust 源代码主要编辑这里4. 编写 Rust 代码导出函数打开src/lib.rs清空默认内容填入以下代码。这段代码的核心任务是告诉 Rust 如何将函数暴露给 Python。文件路径:src/lib.rs/* * 使用 PyO3 编写 Python 扩展模块 * 目标将上一篇的 Rust 词频统计逻辑暴露给 Python */ // 引入 PyO3 预导入模块包含所有必要的宏和类型 use pyo3::prelude::*; use std::collections::HashMap; /// 核心逻辑纯粹的 Rust 函数 /// 这个函数不关心 Python只负责高效的词频计算 /// 输入: 文本字符串和需要返回的 Top N /// 输出: 元组数组 (单词, 次数) fn count_words_rust(text: str, top_n: usize) - Vec(String, u64) { let mut freq_map HashMap::new(); // 使用 Rust 高效的迭代器进行分词和统计 text.split(|c: char| !c.is_alphanumeric()) .filter(|s| !s.is_empty()) .for_each(|word| { *freq_map.entry(word.to_lowercase()).or_insert(0) 1; }); // 排序并截取前 N 名 let mut freq_vec: Vec_ freq_map.into_iter().collect(); freq_vec.sort_by(|a, b| b.1.cmp(a.1)); freq_vec.truncate(top_n); freq_vec } /// Python 接口函数 /// #[pyfunction] 宏标记此函数为 Python 可调用函数 /// 参数类型会自动转换Python str - str, Python int - usize /// 返回值会自动转换Vec(String, u64) - Python list of tuples #[pyfunction] fn count_words(text: str, top_n: usize) - PyResultVec(String, u64) { // 调用 Rust 核心逻辑 let result count_words_rust(text, top_n); // Ok(...) 是 PyO3 的标准返回方式用于处理 Python 异常 Ok(result) } /// 定义 Python 模块 /// #[pymodule] 宏定义模块名称为 rust_analyzer /// _py: Python 解释器的句柄GIL Token /// m: 模块对象本身 #[pymodule] fn rust_analyzer(_py: Python_, m: PyModule) - PyResult() { // 将 count_words 函数注册到模块中 m.add_function(wrap_pyfunction!(count_words, m)?)?; Ok(()) }关键点解析博客重点分离逻辑count_words_rust是纯逻辑count_words是接口层。这样做的好处是 Rust 代码可以不依赖 Python 环境进行单元测试。#[pyfunction]这是魔法发生的地方它让 Rust 函数能被import进来。PyResultRust 的错误处理与 Python 的异常系统对接。5. 编译与安装开发模式这是最关键的一步。我们需要将 Rust 代码编译成二进制文件并链接到当前的 Python 虚拟环境中。在项目根目录pyo3_demo/下执行maturin develop发生了什么cargo开始编译 Rust 代码。生成一个二进制文件如rust_analyzer.cpython-310-x86_64-linux-gnu.so。maturin将这个文件复制到当前虚拟环境的site-packages目录下。看到类似 Built wheel for CPython 3.10和✏️ Installed rust_analyzer的字样就意味着成功了6. 在 Python 中调用现在激动人心的时刻到了。我们可以在 Python 中像使用普通库一样使用刚才编译的 Rust 模块。在项目根目录下创建一个测试文件test.py文件路径:test.py# 直接导入我们刚刚用 Rust 编译的模块 import rust_analyzer def test_rust_function(): print(--- Testing Rust Module ---) # 准备测试数据 text Rust is fast and safe. Python is flexible and easy. Rust and Python together are powerful. top_n 3 # 直接调用 Rust 函数 # PyO3 自动帮我们把 Python str 转成了 Rust str # 并把 Rust 返回的 Vec(String, u64) 转成了 Python list result rust_analyzer.count_words(text, top_n) print(fInput Text: {text}) print(fTop {top_n} words:) for word, count in result: print(f {word}: {count}) if __name__ __main__: test_rust_function()运行测试python test.py预期输出--- Testing Rust Module --- Input Text: Rust is fast and safe. Python is flexible and easy. Rust and Python together are powerful. Top 3 words: rust: 2 python: 2 and: 27. 流程总结与 Mermaid 图示整个流程可以简化为以下几个步骤8. 总结至此我们成功打通了 Rust 到 Python 的调用链路。没有 HTTP 开销函数调用发生在进程内部速度极快。开发体验流畅maturin develop支持增量编译修改 Rust 代码后重新运行该命令即可更新模块。类型安全Rust 的强类型系统在编译期拦截了大量错误Python 端拿到的数据是安全的。