前記事 の続きです.
中規模以上のパッケージの場合, 関数を複数のモジュールに分けたくなると思います (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()
こんにちは, 世界!
>>>