概要
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面白いので、これからも勉強していこうと思います。
今回はこの辺で。
おわり。