Rustのpyo3クレートのメモ。
PyO3は、Pythonから呼べるRustの関数を作成するクレート(ライブラリー)。
PyO3は以下のような手順で使用する。
maturinはPyO3が提供しているツールで、PyO3用のビルドを行う。
〜 [lib] name = "py_example" crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.22.5", features = ["extension-module"] }
[lib]のnameは、Pythonのimport文で指定する名前となる。
ちなみに、この名前にはハイフン「-」を使うことは出来ない。
先にPythonをインストールしておく。
maturin 1.7.4はPython 3.7以降に対応している。
maturinはpipコマンドでインストールする。
pip install maturin
> maturin --version maturin 1.7.4
まず、Rustのプロジェクトを作成する。
maturinを使ってPyO3用のRustプロジェクトを作成することも出来るらしいが、普通のRustプロジェクトでも大丈夫。
Rustプロジェクトを作ったら、Pythonのvenv環境を作っておく。(後から作っても構わない)
cd プロジェクト python -m venv .venv
これにより、「.venv」というディレクトリーが作られる。
Rustで関数を定義する。
use pyo3::prelude::*;
#[pyfunction] fn sum_as_string(a: usize, b: usize) -> PyResult<String> { Ok((a + b).to_string()) }
Pythonから呼び出す関数には#[pyfunction]を付ける。
そして、Pythonでimportするモジュールとなる関数を定義し、その中でPythonから呼び出す関数を登録する。
#[pymodule] fn py_example(m: &Bound<PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(sum_as_string, m)?)?; Ok(()) }
#[pymodule]が付いた関数の関数名は、Cargo.tomlの[lib]のnameと合わせる必要がある。
次に、maturinを使ってビルドする。
cd プロジェクト maturin develop
これにより、Rustのビルドが行われ、Pythonにインストールされる。
(.venv/Lib/site-packagesにライブラリーがインストールされる)
※Pythonのvenv環境が作られていないと、ビルドが失敗する。
実行してみる。
まず、venv環境を起動する。
cd プロジェクト .venv/Scripts/activate
venvのプロンプトが出たら、pythonコマンドを実行する。
python
そしてpyhonの関数を呼び出してみる。
>>> import py_example >>> py_example.sum_as_string(1, 2) '3'
文字列を受け取ってバイト配列を返す例。
Pythonのバイト配列はPyBytesという構造体で表す。
use pyo3::{prelude::*, types::PyBytes};
#[pyfunction] fn to_bytes(py: Python, s: &str) -> PyResult<Py<PyBytes>> { let bytes = s.as_bytes(); let py_bytes = PyBytes::new_bound(py, bytes); Ok(py_bytes.into()) }
PyBytesのインスタンスを作るにはPythonオブジェクト(py)が必要。
これはPythonから呼ばれる関数の第1引数で受け取るように定義する。
pymoduleの方は特に変わり無し。
#[pymodule] fn py_example(m: &Bound<PyModule>) -> PyResult<()> { 〜 m.add_function(wrap_pyfunction!(to_bytes, m)?)?; }
呼び出すPythonの側では、第1引数のpyは指定しなくていい。
>>> import py_example >>> py_example.to_bytes("abc") b'abc'