4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-02

概要

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

これは、PythonからRustを呼んで高速化! PyO3 チュートリアル:簡単な関数をラップする その➁

の続きになります。

目標

Rustのクラス(struct + method)を定義し、それをPythonから呼ぶ
ことを目標にします。

今回は、クラスのパースの仕方と、getter, setterのPyO3経由での呼び方まで解説します。

手順

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

クラスの宣言

lib.rs に以下のように書きます。


//lib.rs

use pyo3::prelude::*;
// ======================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,
}

ここで、


#[pyclass(module = "my_class")]

により、PyO3を経由してPythonから呼べるようにしています。(module = "my_class")はおまじないのように書きましたが、あまり意味がわかっていません。すみません。

ここで、MyClassStructとして定義され、プロパティとして
num: i32 と debug: boolを持っています。

まず、このクラスをPythonのオブジェクトとして呼べるようにするため、コンストラクタを書きます。


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

ここで、implの上に

#[pymethods]

と宣言(デコレート?)されていること、
コンストラクタを示すfn new(num:i32, debug:bool)の上にも

#[new]

と宣言されていること、に注意します。

クラスのモジュールへの追加

これを、以前の関数のように以下のようにモジュールに追加します。


//lib.rs

use pyo3::{wrap_pyfunction};
// =================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(())
}

ここで、

m.add_class::<MyClass>()?;

の行でMyClassをモジュールに追加しています。

getter, setter の追加

今回は、クラスの宣言に加えて、プロパティのgetter, setterに関してもPythonから呼べるように書きます。

//lib.rs

#[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(())
    }
}

このように、#[getter], #[setter]のデコレータを使うことで、PyO3経由でPythonからgetter, setterも呼べます。

今回はプロパティであるnumについてだけ書きました。
getterの返り値に関しては、numの型がi32であることから、Python側はPyResult<i32>として受け取ります。
setterの返り値はないので、PyResult<()>と書けます。

どちらもPythonオブジェクトを引き渡す時は、


Ok(self.num)

Ok(())

を使って引き渡すことは前回と同じです。

setup.py を使ってコンパイル

setup.py, Cargo.toml に関しては前回と同じものですが、念のため書いておくと、

Cargo.toml
[package]
name = "test"
version = "0.1.0"
edition = "2018"

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

[dependencies.pyo3]
version = "0.9.1"
features = ["extension-module"]
setup.py
from setuptools import setup
from setuptools_rust import Binding, RustExtension

setup(name='ope_rust',
        version='0.1',
        rust_extensions=[
            RustExtension('ope_rust', 'Cargo.toml',
                binding=Binding.PyO3)],
            zip_safe=False)

となります。

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)
$ python test.py

test for class
2
4

となり、確かにクラスをコンストラクトでき、プロパティのget,setが実行できたことがわかりました。

まとめ

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

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

次回は、クラスのメソッドをいくつか追加して、色々な型変換を解説できたらと思います。

今回はこの辺で。

おわり。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?