Python与Rust交互(1)

811 阅读2分钟

0x00 开篇

Rust 可以与很多语言交互,前面我们已经介绍过与 C#JavaScript,本篇文章将介绍下 Rust 如何与 Python 交互。

0x01 PyO3

PyO3 是一个 Rust 的库。通过它,使得我们从 Python 调用 Rust 变得非常容易。它可以用于创建本机 Python 扩展模块的工具。还支持从 Rust 二进制文件运行 Python 代码并与之交互。

另外,如果要将 rust 编译为 puthon 模块,还需要安装 maturin。安装方法如下:

pip install maturin   

PyO3 支持需要以下环境:

注:本文的所有操作默认你已经安装 RustPython 环境。

  • Python 3.7 及更高版本(CPython 和 PyPy)
  • Rust 1.48 及更高版本

0x02 Python 调用 Rust 函数

toml 配置

我们需要创建一个 Rust 为 lib 项目,crate-type 必须为 cdylib。截至写文章时,pyo3 的最新版本是 0.18.3。完整的 toml 配置如下:

[package]
name = "python_rust"
version = "0.1.0"
edition = "2021"[lib]
name = "python_rust"
crate-type = ["cdylib"]
​
[dependencies]
pyo3 = "0.18.3"
编写代码

为了简单测试,我们使用 Rust 写一个求和的代码。写函数时跟我们平时写没啥区别,但是返回值需要返回 PyResult,并且还需要标注 #[pyfunction]#[pyfunction] 属性用于从 Rust 函数定义 Python 函数。定义后,需要使用 wrap_pyfunction! 宏将该函数添加到模块中。

我们还需要创建一个与 toml 配置文件中 lib.name 同名函数(当前也可以使用 #[pyo3(name = "custom_name")] 覆盖模块名称),并标注为 #[pymodule]#[pymodule] 过程宏负责将模块的初始化函数导出到 Python。

完整的示例代码如下:

use pyo3::prelude::*;
​
/// 求两个数的和
#[pyfunction]
fn sum(a: isize, b: isize) -> PyResult<isize> {
    Ok(a + b)
}
​
/// 一个用Rust实现的Python模块。
///
/// 这个函数的名字必须与`Cargo.toml`中的`lib.name`匹配
#[pymodule]
fn python_rust(_py: Python, module: &PyModule) -> PyResult<()> {
    module.add_function(wrap_pyfunction!(sum, module)?)?;
    Ok(())
}
编译并安装

官方推荐使用虚拟环境安装模块,防止与其它模块冲突。我这里还是喜欢使用 maturin build 先编译,然后手动安装。

  • 运行 maturin build 编译
  • 使用 pip 安装 target/wheels/模块名称.whl
在 python 中使用函数

编写测试代码:

import python_rust
​
sum = python_rust.sum(5, 6)
print(sum)

成功输出结果 11 。有没有感觉到很简单呢。

定义多个函数

当然了,我们还可以定义多个函数,我们只需要在模块函数中 add_function 就可以了。代码如下:

use pyo3::prelude::*;
​
/// 求两个数的和
#[pyfunction]
fn sum(a: isize, b: isize) -> PyResult<isize> {
    Ok(a + b)
}
​
#[pyfunction]
fn multiple(a: isize, b: isize) -> PyResult<isize> {
    Ok(a * b)
}
​
/// 一个用Rust实现的Python模块。
///
/// 这个函数的名字必须与`Cargo.toml`中的`lib.name`匹配
#[pymodule]
fn python_rust(_py: Python, module: &PyModule) -> PyResult<()> {
    module.add_function(wrap_pyfunction!(sum, module)?)?;
    module.add_function(wrap_pyfunction!(multiple, module)?)?;
    Ok(())
}

定义新函数后,再次使用模块,要记得先卸载再重新安装!

0x03 小结

本篇文章简单介绍了 Rust 使用 PyO3 来编写 python 模块,其实 PyO3 的功能还有很多,接下来我们将继续介绍如果在Rust中使用不同的类型。