如何使用Rust扩展Python:第1部分

15 篇文章 3 订阅

Python很棒,但是我发现在我没有其他语言可用的情况下,没有多少Pythonic聪明能让一些代码运行得足够快。我想要学习Rust的主要原因之一是为了获得比C更好的东西。

Rust不仅具有各种优势,使其成为需要快速正确运行的代码的良好选择,而且还有一些相当不错的板条箱(库)可以很好地与Python连接。

这是一个小教程,向您展示从Python调用简单的Rust函数是多么容易。如果你想自己尝试一下,你会在GitHub上找到代码

先决条件 我在本教程中假设您已经熟悉编写Python脚本并导入和使用包,并且您习惯使用命令行。你还需要安装Rust

Rust操作

将编译代码编入Python的最快方法是使用builtin ctypes包。这是Python的“外部函数接口”或FFI:一种在您用来进行调用的语言之外调用函数的方法。

ctypes允许我们在共享库1中调用任意函数,只要这些函数符合某些标准C语言调用约定。值得庆幸的是,Rust努力让我们很容易构建这样一个共享库。

首先要做的是创建一个带有货物的新项目,即Rust构建工具:

$ cargo new rustfrompy
     Created library `rustfrompy` project

$ tree
.
├── Cargo.toml
└── src
    └── lib.rs

1 directory, 2 files

在旁边
我使用相当普遍的约定,固定宽度字体中设置的文本是示例代码或输入的命令。对于后者, 键 在 您 键 入 的 命 令 ( 省 略 键在您键入的命令(省略 )之前,而行不在以$从上一个命令输出。我假设对Unix风格的命令行有一个基本的熟悉,但如果你需要了解更多,我应该提供一些资源链接!

我们需要编辑Cargo.toml文件并添加[lib]部分:

[package]
name = "rustfrompy"
version = "0.1.0"
authors = ["Jez Cope <j.cope@erambler.co.uk>"]

[dependencies]

[lib]
name = "rustfrompy"
crate-type = ["cdylib"]

这告诉货物我们想要制作一个C兼容的动态库(crate-type = [“cdylib”])以及要调用它的内容,以及一些标准元数据。然后我们可以将代码放在src / lib.rs中。

我们只使用一个简单的玩具功能,将两个数字加在一起:

#[no_mangle]
pub fn add(a: i64, b: i64) -> i64 {
    a + b
}

请注意pub关键字,它指示编译器使其他模块可以访问此函数,以及#[no_mangle]注释,它告诉它使用函数的标准C命名约定。如果我们不这样做,那么Rust将为它自己的邪恶目的生成一个新的函数名称,作为副作用,当我们想要从Python中使用它时,我们将不知道该怎么称呼它。

作为优秀的开发人员,我们还要添加一个测试:

#[cfg(test)]
mod test {
    use ::*;

    #[test]
    fn test_add() {
        assert_eq!(4, add(2, 2));
    }
}

我们现在可以运行货物测试,它将编译该代码并运行测试:

$ cargo test
   Compiling rustfrompy v0.1.0 (file:///home/jez/Personal/Projects/rustfrompy)
    Finished dev [unoptimized + debuginfo] target(s) in 1.2 secs
     Running target/debug/deps/rustfrompy-3033caaa9f5f17aa

running 1 test
test test::test_add ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

一切正常!现在只是为了构建共享库,我们可以尝试从Python调用它:

$ cargo build
   Compiling rustfrompy v0.1.0 (file:///home/jez/Personal/Projects/rustfrompy)
    Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs

请注意,构建是未优化的并包含调试信息:这在开发中很有用,但是一旦我们准备好使用我们的代码,如果我们使用optimisations进行编译它将会运行得更快。货物让这很容易:

Python操作

毕竟,Python位非常短。首先我们导入ctypes包(它包含在所有最近的Python版本中):

from ctypes import cdll

Cargo已将我们的共享库整理到一个文件夹中,因此我们需要告诉Python从何处加载它。在Linux上,它将被称为lib .so,其中“something”是来自Cargo.toml的crate名称,“rustfrompy”:
注:windows上是rustfrompy.dll

lib = cdll.LoadLibrary('target/release/librustfrompy.so')

最后,我们可以随心所欲地调用该函数。这是一个pytest风格的测试:

def test_rust_add():
    assert lib.add(27, 15) == 42

如果你安装了pytest(你应该!),你可以像这样运行整个测试:

$ pytest --verbose test.py
====================================== test session starts ======================================
platform linux -- Python 3.6.4, pytest-3.1.1, py-1.4.33, pluggy-0.4.0 -- /home/jez/.virtualenvs/datasci/bin/python
cachedir: .cache
rootdir: /home/jez/Personal/Projects/rustfrompy, inifile:
collected 1 items

test.py::test_rust_add PASSED

有效!如果你想亲自尝试一下,我已经把rust和Python代码放在​​github上了

缺点

好的,所以这是一个非常简单的例子,我掩饰了很多东西。例如,如果我们执行lib.add(2.0,2)会发生什么?这会导致Python抛出错误,因为我们的Rust函数只接受整数(64位有符号整数,i64,确切地说),我们给它一个浮点数。 ctypes无法猜测给定函数将使用哪种类型,但它至少可以告诉我们何时出错。

为了正确解决这个问题,我们需要做一些额外的工作,告诉ctypes库每个函数的参数和返回类型是什么。对于更复杂的库,可能会有更多的内务处理,例如将函数的返回代码转换为更多Pythonic风格的错误。

对于像这样的小例子,没有太大的问题,但是编译的库越大,Python方面需要更多的额外样板才能使用所有函数。当你使用现有的库时,你没有太多选择,但是如果你是从头开始构建它专门用于与Python接口,那么使用Python C API就有更好的方法。你可以直接在Rust中调用它,但是有一些Rust条件可以让生活更轻松,我将在未来的博客文章中看一下这些。

备注:Linux上的.so,Mac上的.dylib和Windows上的.dll

源文出处:https://erambler.co.uk/blog/extending-python-rust-1/

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值