4.1 python中调用rust程序
阅读原文时间:2023年07月08日阅读:1

概述

使用rust-cpython将rust程序做为python模块调用;

通常为了提高python的性能;

参考

https://github.com/dgrunwald/rust-cpython

创建rust lib库

cargo new rust2py --lib

或者使用IDE创建一个rust lib库项目

Cargo.toml

[package]
name = "rust2py"
version = "0.1.0"
edition = "2018"

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

[dependencies.cpython]
version = "0.3"
features = ["extension-module"]

lib.rs

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

#[macro_use]
extern crate cpython;

use cpython::{PyResult, Python, py_module_initializer, py_fn};

pub fn print_str(a: String) -> String {
print!("{:#?}",a);
a
}

pub fn print_str_py(_: Python, a: String) -> PyResult{
let mm = print_str(a);
Ok(mm)
}

// logic implemented as a normal rust function
fn sum_as_str(a:i64, b:i64) -> String {
format!("{}", a + b).to_string()
}

// rust-cpython aware function. All of our python interface could be
// declared in a separate module.
// Note that the py_fn!() macro automatically converts the arguments from
// Python objects to Rust values; and the Rust return value back into a Python object.
fn sum_as_str_py(_: Python, a:i64, b:i64) -> PyResult {
let out = sum_as_str(a, b);
Ok(out)
}

py_module_initializer!(rust2py, init_rust2py, PyInit_rust2py, |py, m| {
m.add(py, "__doc__", "This module is implemented in Rust.")?;
m.add(py, "print_str", py_fn!(py, print_str_py(a: String)))?;
m.add(py, "sum_as_str", py_fn!(py, sum_as_str_py(a: i64, b:i64)))?;
Ok(())
});

注意:py_module_initializer方法的参数的中rust2py一定要与模块的名称一致,这个不是随便写的字符串名称,比如PyInit_rust2py就表示将来在python中调用的模块名称是rust2py

编译并复制到python的模块

cargo build
cp target/debug/librust2py.so /opt/app/anaconda3/lib/python3.8/site-packages/rust2py.so

注意:复制到python模块的so没有lib前缀

可以换一个正规的python模块名称, 效果是一样的, 但这样的名字看起来更"专业"一点 ^_^

ai@aisty:/opt/app/anaconda3/envs/py37/lib/python3.7/site-packages$ mv rust2py.so rust2py.cpython-37m-x86_64-linux-gnu.so
ai@aisty:/opt/app/anaconda3/envs/py37/lib/python3.7/site-packages$ ll rust2py.cpython-37m-x86_64-linux-gnu.so
-rwxrwxr-x 1 ai ai 5101552 12月 2 10:52 rust2py.cpython-37m-x86_64-linux-gnu.so*

封装一个自动安装的脚本install.sh

#!/bin/bash

cd /opt/wks/rust/rfil/rust2py/
/home/ai/.cargo/bin/cargo build
cp target/debug/librust2py.so /opt/app/anaconda3/envs/py37/lib/python3.7/site-packages/rust2py.cpython-37m-x86_64-linux-gnu.so

每次修改,执行一下脚本就会覆盖上一次的结果

(py37) ai@aisty:/opt/wks/rust/rfil/rust2py$ chmod +x install.sh
(py37) ai@aisty:/opt/wks/rust/rfil/rust2py$ ./install.sh

其他安装参考

https://github.com/PyO3/setuptools-rust

python调用模块

ai@aisty:/opt/app/anaconda3/lib/python3.8/site-packages$ python3.8
Python 3.8.5 (default, Sep 4 2020, 07:30:14)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.

import rust2py
rust2py.sum_as_str(2,5)
'7'
rust2py.print_str("from rust")
'from rust'

接下来添加一个稍微复杂的方法:统计列表中元素的个数,输入Python列表,返回Python字典

fn elem_count(py: Python, pl: PyList) -> PyResult {
let dt = PyDict::new(py);
for e in pl.iter(py) {
let el = &e;
let ct = dt.contains(py,el).unwrap();

    if ct {  
        let a = dt.get\_item(py,el).unwrap().extract::<i32>(py).unwrap() + 1 ;  
        dt.set\_item(py,el,a)?;  
    }else {  
        dt.set\_item(py,el,1)?;  
    }

}  
Ok(dt)  

}

// https://dgrunwald.github.io/rust-cpython/doc/src/cpython/objects/dict.rs.html#129-143

py_module_initializer!(rust2py, init_rust2py, PyInit_rust2py, |py, m| {
m.add(py, "__doc__", "This module is implemented in Rust.")?;
m.add(py, "print_str", py_fn!(py, print_str_py(a: String)))?;
m.add(py, "sum_as_str", py_fn!(py, sum_as_str_py(a: i64, b:i64)))?;
m.add(py, "hello", py_fn!(py, hello_py()))?;
m.add(py, "elem_count", py_fn!(py, elem_count(pl: PyList)))?;
Ok(())
});

rust中的python方法通常返回一个PyResult,这是一个Python对象或Python异常的枚举,使用.unwrap()将之解析为一个Python对象,然后就可以调用Python对象的方法了,这些方法可以从后面介绍的文档上查看

>>> import rust2py

rust2py.elem_count([1,2,3])
{1: 1, 2: 1, 3: 1}
rust2py.elem_count([1,2,3,3])
{1: 1, 2: 1, 3: 2}

更多数据类型方法请参考

http://dgrunwald.github.io/rust-cpython/doc/cpython/

如果想知道更多的关于如何使用一个Py对象的细节,请看上面文件源码

每个py对象,后面都有一个[src]的标记,这是个超链接,点开之后会转向源码,比如PyDict,源码中有测试代码,对用法学习很有帮助

https://dgrunwald.github.io/rust-cpython/doc/src/cpython/objects/dict.rs.html#129-143

#\[test\]  
fn test\_items\_list() {  
    let gil = Python::acquire\_gil();  
    let py = gil.python();  
    let mut v = HashMap::new();  
    v.insert(7, 32);  
    v.insert(8, 42);  
    v.insert(9, 123);  
    let dict = v.to\_py\_object(py);  
    // Can't just compare against a vector of tuples since we don't have a guaranteed ordering.  
    let mut key\_sum = 0;  
    let mut value\_sum = 0;  
    for el in dict.items\_list(py).iter(py) {  
        let tuple = el.cast\_into::<PyTuple>(py).unwrap();  
        key\_sum += tuple.get\_item(py, 0).extract::<i32>(py).unwrap();  
        value\_sum += tuple.get\_item(py, 1).extract::<i32>(py).unwrap();  
    }  
    assert\_eq!(7 + 8 + 9, key\_sum);  
    assert\_eq!(32 + 42 + 123, value\_sum);  
}

看源码,是最直接,直达本质的快捷学习通道!

全代码

#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

#[macro_use]
extern crate cpython;

use cpython::{PyResult, Python, PyDict, py_module_initializer, py_fn, PyList};

pub fn print_str(a: String) -> String {
print!("{:#?}",a);
a
}

pub fn print_str_py(_: Python, a: String) -> PyResult{
let mm = print_str(a);
Ok(mm)
}

// logic implemented as a normal rust function
fn sum_as_str(a:i64, b:i64) -> String {
format!("{}", a + b).to_string()
}

// rust-cpython aware function. All of our python interface could be
// declared in a separate module.
// Note that the py_fn!() macro automatically converts the arguments from
// Python objects to Rust values; and the Rust return value back into a Python object.
fn sum_as_str_py(_: Python, a:i64, b:i64) -> PyResult {
let out = sum_as_str(a, b);
Ok(out)
}

fn hello_py(py: Python) -> PyResult {
let sys = py.import("sys")?;
let version: String = sys.get(py, "version")?.extract(py)?;

let locals = PyDict::new(py);  
locals.set\_item(py, "os", py.import("os")?)?;  
let user: String = py.eval("os.getenv('USER') or os.getenv('USERNAME')", None, Some(&locals))?.extract(py)?;

let res = format!("Hello {}, I'm Python {}", user, version).to\_string();  
let res = res.replace("\\n","");  
Ok(res)  

}

fn elem_count(py: Python, pl: PyList) -> PyResult {
let dt = PyDict::new(py);
for e in pl.iter(py) {
let el = &e;
let ct = dt.contains(py,el).unwrap();
// let ct2 = match ct {
// Ok(b) => b,
// Err(e) => return Err(e),
// };

    if ct {  
        let a = dt.get\_item(py,el).unwrap().extract::<i32>(py).unwrap() + 1 ;  
        dt.set\_item(py,el,a)?;  
    }else {  
        dt.set\_item(py,el,1)?;  
    }

}  
Ok(dt)  

}

// https://dgrunwald.github.io/rust-cpython/doc/src/cpython/objects/dict.rs.html#129-143

py_module_initializer!(rust2py, init_rust2py, PyInit_rust2py, |py, m| {
m.add(py, "__doc__", "This module is implemented in Rust.")?;
m.add(py, "print_str", py_fn!(py, print_str_py(a: String)))?;
m.add(py, "sum_as_str", py_fn!(py, sum_as_str_py(a: i64, b:i64)))?;
m.add(py, "hello", py_fn!(py, hello_py()))?;
m.add(py, "elem_count", py_fn!(py, elem_count(pl: PyList)))?;
Ok(())
});

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章