概要

1. Pyhtonでアルゴリズムまで書いてあるのは速度面では好ましくないな〜
2. よし、C, C++あたりで書いてあるものを探して、それをPythonから呼んで高速化しよう。
3. なかなかいいライブラリ見つからんな、
4. おっ、Rustていう言語で書かれてるのならあったぞ
5. RustてPythonから呼べんのか??

これは、PythonからRustを呼んで高速化! PyO3 チュートリアル:クラスをラップする その➀

の続きになります。

目標

目標は、
Rustのクラス(struct + method)を定義し、それをPythonから呼ぶ
でしたが、
前回でRustで書かれたクラスをPythonから呼ぶ方法を解説しました。

クラスのコンストラクタおよび、プロパティのgetter,setterもPythonからPyO3経由で呼ぶことができました。

今回は、クラスメソッドをいくつか追加して、RustからPythonへの型にPyO3経由で変換するところを解説していきます。

PyO3のgitレポジトリ(ここ)を参照し、

RustのオブジェクトをPython側にどうPyO3で引き渡すかを解説します。

Vec -> List

Hashmap -> Dict

Vec -> Tuple

などです。

手順

前回までにcargo new –lib exampleで作ったプロジェクトを使用します。
新しく作ってももちろん問題ありません。

おさらいとして、前回はクラスのコンストラクタ、プロパティのnumのgetter, setter をPyO3経由でPythonから呼べるようにしました。
以下が、前回完成させたコードです。


//lib.rs
use pyo3::prelude::*;
use pyo3::{wrap_pyfunction};


// ======================RUST CLASS TO PYTHON ======================================
/// Class for demonstration
// this class, MyClass can be called from python
#[pyclass(module = "my_class")]
struct MyClass {
   num: i32,
   debug: bool,
}

#[pymethods]
impl MyClass{
    #[new]
    fn new(num:i32, debug:bool) -> Self{
        MyClass{
            num: num,
            debug: debug
        }
    }

    #[getter]
    fn get_num(&self) -> PyResult<i32>{
        Ok(self.num)
    }

    #[setter]
    fn set_num(&mut self, num: i32) -> PyResult<()>{
        self.num = num;
        Ok(())
    }
}


// =================CREATING MODULE FOR PYTHON=========================
/// This module is a python module implemented in Rust.
#[pymodule]
fn test_library(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_wrapped(wrap_pyfunction!(get_prime_numbers))?;
    m.add_class::<MyClass>()?;

    Ok(())
}

このMyClassに、今回は関数を6つ追加します。
早速ですが、以下がコードです。


//lib.rs

use pyo3::types::PyType;
use pyo3::types::PyInt;
use pyo3::types::PyList;
use pyo3::types::PyTuple;
use pyo3::types::PyDateTime;
use std::collections::HashMap;

#[pymethods]
impl MyClass{

    fn test1(&self) -> PyResult<bool>{
        if self.num > 3{
            Ok(true)
        }else{
            Ok(false)
        }
    }

    fn test2(&self) -> PyResult<String>{
        if self.debug == true{
            let result: &str = "your debug is True";
            Ok(result.to_string())
        }else{
            let result: &str = "your debug is False";
            Ok(result.to_string())
        }
    }

    fn test3<'py>(&self, py: Python<'py>) -> PyResult<&'py PyList>{
        let mut vec = vec![1,2,3];
        let result = PyList::new(py, &vec);
        Ok(result)
    }

    fn test4(&self, py: Python) -> PyResult<PyObject>{
        let mut map = HashMap::new();
        map.insert("key1", 1);
        map.insert("key2", 2);
        map.insert("key3", 3);
        assert_eq!(map["key1"], 1);
        assert_eq!(map["key2"], 2);
        assert_eq!(map["key3"], 3);
        Ok(map.to_object(py))

    }

    fn test5(&self) -> PyResult<f64>{
        let result:f64 = 1.23;
        Ok(result)
    }

    fn test6<'py>(&self, py: Python<'py>, dt: &PyDateTime) -> PyResult<&'py PyTuple>{
        let mut vec = vec![3,4,5];
        let result = PyTuple::new(py, &vec);
        Ok(result)

    }
}

fn test1

Rustのboolを、PythonのboolへとPyO3経由で受け渡ししています。

fn test2

RustのStringを、PythonのstrへとPyO3経由で受け渡ししています。

fn test3

RustのVecを、PythonのListへとPyO3経由で受け渡ししています。
この書き方がきちんと理解できてません。。

fn test4

RustのHashmapを、PythonのDictへとPyO3経由で受け渡ししています。

fn test5

Rustのf64を、PythonのfloatへとPyO3経由で受け渡ししています。

fn test6

RustのVecを、PythonのtupleへとPyO3経由で受け渡ししています。この際、関数の引数として、PythonのDatetimeを受け取っています。
この書き方がきちんと理解できてません。。

testを実行する

test3, test6に関しては、正直書き方がうまく理解できていませんが、テストを実行します。
前回と同じようにCargo.toml、setup.pyを用意することで、

python setup.py install

でビルドすることができます。

その後、test.pyを以下のように準備し、テストを実行します。


import test_library 


if __name__  == "__main__":

    # Testing class
    print("\ntest for class")
    num = 2
    debug = True
    test = test_library.MyClass(num=num, debug=debug)

    print(test.num) # getter test
    test.num = 4    # setter test
    print(test.num)
    result = test.test1()
    print(result)
    print(type(result))

    result = test.test2()
    print(result)
    print(type(result))

    result = test.test3()
    print(result)
    print(type(result))

    result = test.test4()
    print(result)
    print(type(result))

    result = test.test5()
    print(result)
    print(type(result))

    import datetime 
    now = datetime.datetime.now()
    result = test.test6(now)
    print(result)
    print(type(result))

まとめ

今回は、クラスメソッドをいくつか追加して、RustからPythonへの型にPyO3経由で変換するところを解説しました。
Cythonよりは比較的わかりやすい感覚があるものの、PyO3自体バージョンがどんどん更新されているため、一番いいのはバージョンをきちんと意識して(Fixして)開発するか、
もしくはGitをきちんと追い、APIの呼び方の変更にきちんと気を配るべきでしょう。

ただ、Rust面白いので、これからも勉強していこうと思います。

今回はこの辺で。

おわり。

广告
将在 10 秒后关闭
bannerAds