前記事 の続きです.
中規模以上のパッケージの場合, 関数を複数のモジュールに分けたくなると思います (SciPy の scipy.optimize
, scipy.integrate
のように). ほぼ前回の記事通りですが, 若干ハマった部分があるので詳しく書いておきます.
モジュールの作成
具体的に英語と日本語で hello world を出力する language
パッケージをつくってみます. まずはモジュールを普通につくります.
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[pyfunction]
fn hello() -> PyResult<()> {
println!("Hello, world!");
Ok(())
}
#[pymodule]
fn english(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(hello))?;
Ok(())
}
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[pyfunction]
fn hello() -> PyResult<()> {
println!("こんにちは, 世界!");
Ok(())
}
#[pymodule]
fn japanese(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(hello))?;
Ok(())
}
ここで, 本体 (src/lib.rs
) から参照することになる english
, japanese
関数に pub
を付ける必要はありません. というのも, #[pymodule]
アトリビュートによりラッパー関数が自動的に生成され, そちらを読み込みに行くからです. その結果, 本体は次のようなコードになります.
use pyo3::prelude::*;
use pyo3::wrap_pymodule;
mod en;
use en::*;
mod ja;
use ja::*;
#[pymodule]
fn language(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(english))?;
m.add_wrapped(wrap_pymodule!(japanese))?;
Ok(())
}
ここで例えば use en::english;
とするとコンパイルエラーになります. これは, 上で指摘したように, language
関数内で en::english
関数を呼び出しているように見えますが, wrap_pymodule
マクロのために実際に呼び出している関数は en::PyInit_english
関数だからです (なのでこの関数を直接 use
しても動きます).
Python から呼び出し
Cargo.toml
やコンパイル, .so
ファイルの配置などは前記事通りです.
$ python3
Python 3.7.2 (default, Jan 24 2019, 15:36:28)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import language
>>>
>>> language.english.hello()
Hello, world!
>>> language.japanese.hello()
こんにちは, 世界!
>>>