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