龙空技术网

如何在你的项目中混合 Rust 和 Python

杨同学编程 191

前言:

目前朋友们对“python多个项目如何共用代码”大致比较珍视,朋友们都想要了解一些“python多个项目如何共用代码”的相关资讯。那么小编在网上网罗了一些关于“python多个项目如何共用代码””的相关内容,希望兄弟们能喜欢,咱们一起来了解一下吧!

在在 2021 年Stack Overflow 年度开发者调查[1] 中,Rust 被评为“最受欢迎的语言”(连续第六年!),Python 被评为“最受欢迎”的语言。那么为什么不混合两种语言呢!本次介绍将建立一个结合 Python 和 Rust 代码的混合项目。

为什么?Python 解释方法和动态类型加速了我们的开发工作流程。但是对于更复杂的工作,我们经常会遇到性能问题。

Rust 是实现 Python 项目本机性能升级的理想选择。本机优化使用低级语言和编译器绕过 Python 解释器。

通过结合 Rust 和 Python,您可以获得两全其美:使用 Python 的快速交互式开发环境和使用 Rust 的本机性能。

Python + Rust 项目 — 作者的图片

为什么在同一个项目中使用两种语言?

在以下部分中,我将设置一个混合 Python/Rust 项目的示例。我们的项目将在同一个项目和工件中结合 Python 代码和 Rust。

混合项目的好处是显而易见的:您在同一个存储库中共享您的 Rust 和 Python 代码并创建一个工件。结果,您将两种语言结合在同一个项目中;使用原生 Rust 组件迭代和扩展 Python 代码变得更加容易。此外,只需部署一个工件,就可以更轻松地维护和共享您的项目。

缺点很明显:两种语言在同一个目录下,如何构建、测试和部署这样的项目?

如何?PyO3 + 成熟素

集成 Rust 和 Python 的主要方式是PyO3 框架。使用 PyO3,我们可以用 Rust 编写原生 Python 模块。当然,该框架也支持从 Rust 调用 Python,但我将只关注使用原生 Rust 模块扩展 Python。

PyO3 将您的 Rust 代码包装到本机 python 模块中。因此,绑定生成很容易且完全透明。

棘手的部分是构建混合 Python 和本机代码项目(Python + [Rust、C 或 C++])时代码的打包。那么如何制作集成 Python 和原生代码的轮子呢?

Python代码无需编译即可分发,与平台无关;安装轮子会.pyc即时创建文件(Python 字节码)。但是我们的 Rust 代码需要编译和分发为共享库(二进制代码)。

打包是这个项目的难点:创建一种通用的、多平台的方式来生成混合 Python-Rust 包。

可以帮助您实现此目的的工具是Maturin。Maturin 管理创建、构建、打包和分发混合 Python/Rust 项目。

PyO3 + Maturin — 图片作者

使用 Maturin 初始化一个混合项目

首先,使用pip.

$ pip 安装成熟

它包括maturin二进制文件,一个命令行界面。

$ maturin --help maturin 0.12.9使用 pyo3、rust-cpython 和 cffi 绑定以及 rust 二进制文件作为python 包构建和发布 crates用法:    maturin <SUBCOMMAND>选项:    -h,--help 打印帮助信息    -V, --version 打印版本信息SUBCOMMANDS:     build 将 crate 构建到 python 包    中 develop 将 crate 作为模块安装在当前 virtualenv     help 打印此消息或给定子命令的帮助    init 在现有目录    列表中创建一个新的货物项目-python 搜索并列出可用的 python 安装    new 创建一个新的 cargo 项目    publish 构建并将 crate 作为 python 包发布到 pypi     sdist 只构建一个源分发(sdist)而不编译    上传 将 python 包上传到 pypi

可执行文件提出了maturin几个选项。让我们放大构建和开发。

build:编译 Rust 并将其与 Python 包集成。它生成一个 Wheel 包,将 Rust 生成的二进制工件与最终的 Python 代码混合在一起。develop:在开发和调试项目时很有用。此命令直接在 Python 模块中构建和安装新创建的共享库。

为了测试一个混合的 Rust-Python 项目,我们现在可以使用maturin new命令初始化我们的库。

$ maturin new --help maturin-new创建一个新的货物项目用法:    maturin new [OPTIONS] <PATH> ARGS:     <PATH> 项目路径OPTIONS:     -b, --bindings <BINDINGS> 使用哪种绑定 [可能values: pyo3, rust-cpython,                                  cffi, bin]     -h, --help 打印帮助信息        --mixed 使用混合Rust/Python项目布局        --name <NAME> 设置生成的包名,默认为目录名

对于我们的项目,我们使用选项--bindings pyo3和--mixed my_project. 该my_project参数与此示例的目标项目目录匹配。

$ maturin new --bindings pyo3 --mixed my_project

生成的项目将目录中的 Python 包my_project与 Rust 项目定义Cargo.toml和基于 Rust 的src目录集成在一起。

$ tree my_project my_project ├── Cargo.toml ├── my_project │ └── __init__.py ├── pyproject.toml ├── src │ └── lib.rs └── test     └── test.py 3个目录, 5 个文件

好的,我们有了基本的项目骨架。我们现在可以添加一个简单的 Rust 函数来公开。

项目结构 — 作者图片

用 PyO3 包装 Python 代码

我们需要在 Python 运行时可调用的 Python 模块中声明和导出 Rust 函数。我们首先使用#[pymodule]Rust 宏创建一个模块。在我们的函数中,my_project我们将声明 Rust 和 Python 之间的绑定。

→ 锈«my_project/src/lib.rs» =使用pyo3::prelude::*; (1) (2) <<functions>>#[pymodule] fn my_project(_py: Python , m: & PyModule ) -> PyResult <()> {     (3)     <<function_declarations>>    Ok(())}   ( 4) <<测试>>
(1)我们包括Py03定义和宏。(2)在<<functions>>块中,我们声明了我们的 Rust 函数。(3)在<<function_declarations>>块中,我们在最终的 Python 模块中公开我们的 Rust 函数。(4)在<<tests>>块中,我们添加了 Rust 单元测试函数。一个简单的函数

让我们从<<functions>>块的简单函数开始。我们的导出函数is_prime通过将其除以前面的数字来检查其输入的素数。对于数字num,我们检查 2 和 之间数字的除法余数√num。

→ rust «functions» = #[pyfunction] (1) fn is_prime(num: u32) -> bool {     match num {        0 | 1 => false ,        _ => {             let limit = (num as f32).sqrt() as u32 ; (2)             (2..=limit).any(|i| num % i == 0) == false  (3)         }    }}
(1) Rust 宏#[pyfunction]为 Python 绑定生成代码。(2)计算我们的试除数系列的上限。(3)生成试验并测试除法的其余部分。2..=limit生成介于2和limit(包括)之间的范围。any检查生成的元素之一是否满足谓词。

在<<function_declarations>>块中,我们将函数添加到导出的模块中。

→ 锈«function_declarations» = m.add_function(wrap_pyfunction!(is_prime, m)?)?;

在该<<tests>>块中,我们添加了一些简单的单元测试。

→ rust «tests» = #[cfg(test)] mod 测试{    使用 super ::*;     #[测试]     fn simple_test_false() {        assert_eq!(is_prime(0), false );         assert_eq!(is_prime(1), false );         assert_eq!(is_prime(12), false )    }    #[test]     fn simple_test_true() {        assert_eq!(is_prime(2), true );         assert_eq!(is_prime(3), true );         assert_eq!(is_prime(41), true )    }}
构建并运行您的 Python 模块

使用 Maturin,构建一个原生 Rust 模块并将其导出到 Python 解释器中只需一行代码。

$ cd my_project $ maturin 开发

该命令构建 Rust 原生模块并将其部署在当前的 virtualenv 中。

导入 我的项目打印(我的项目.is_prime (12))打印(我的项目.is_prime (11))>假>真

在引擎盖后面,命令maturin develop:

使用 Cargo 编译原生 Rust 模块:在本地 python 模块中编译和复制共享库。安装 Python 模块:该模块安装在您的 virtualenv 中。对于无需在每次 Python 代码更改时重新构建项目的即时测试,您可以通过在( Maturin Editable Installs )中添加以下行来使用可编辑安装(ie )。pip install -epyproject.yml

[构建系统]需要 = [ "maturin>=0.12" ]构建后端 = "maturin"
测试你的模块

我们可以在 Python 中添加一个简单的基于属性的测试来检查is_primeRust 函数的行为。

我们首先为Hypothesis Python 包添加一个依赖项。要将依赖项添加到我们的项目中,我们可以编辑该pyproject.toml文件。Maturin 支持PEP 621,启用 Python 元数据规范。

→ toml «my_project/pyproject.toml» = [build-system] requires = [ "maturin>=0.12" ]build-backend = "maturin" [project.optional-dependencies] test = [   "hypothesis" ,   "sympy" ]   [project] name = " my_project " requires-python = ">=3.6"分类器 = [     "Programming Language :: Rust" ,     "Programming Language :: Python :: Implementation :: CPython" ,]

我们现在可以运行maturin develop命令了。

$ cd my_project $ maturin 开发 --extras 测试(1)

(1)使用--extras testMaturin 选项安装 Python 测试依赖项。

我们添加了一个简单的基于属性的测试。

→ Python «my_project/test/test.py» =从 假设 导入设置,详细程度,从 假设 导入策略作为st from  sympy.ntheory  import isprime import  my_project @given(s=st.integers(min_value=1, max_value=2 **10))@settings(verbosity=Verbosity.normal, max_examples=500) def test_is_prime(s):     assert isprime(s) == my_project.is_prime(s)   if __name__ == "__main__" :       test_is_prime()

我们终于可以运行基于属性的 Python 测试了。

$ cd my_project $ python test/test.py

还有我们的 Rust 测试。

$ cd my_project $ 货物测试

标签: #python多个项目如何共用代码