S-JIS[2025-12-13/2025-12-18]

pyo3 クラス定義

Rustpyo3でクラスを定義する方法のメモ。


概要

Pythonでクラスとして扱うものをPyO3 0.27で定義するには、structに#[pyclass]を付け、implに#[pymethods]を付ける。


Rust側でMyClassを定義する。

example-pyo3/src/lib.rs:

use pyo3::prelude::*;

#[pymodule]
mod example_pyo3 {
    use pyo3::{prelude::*};

    #[pyclass]
    struct MyClass {
        #[pyo3(get, set)]
        value: String,
    }

    #[pymethods]
    impl MyClass {
        #[new]
        fn new(value: String) -> Self {
            Self { value }
        }

        #[getter]
        fn my_value(&self) -> &str {
            &self.value
        }

        fn my_method(&self) -> &str {
            &self.value
        }
    }

    #[pyfunction]
    fn create_my_class(value: String) -> MyClass {
        MyClass { value }
    }
}

呼び出すPython側

call-pyo3/main.py:

import example_pyo3

def main():
    print("Hello from call-pyo3!")

    c = example_pyo3.create_my_class("abc") // MyClassインスタンスを返す関数の呼び出し
    print(c.value)
    print(c.my_value)
    print(c.my_method())

    m = example_pyo3.MyClass("zzz") // MyClassコンストラクターの呼び出し
    print(m.value)
    print(m.my_value)
    print(m.my_method())
    m.value = "def"
    print(m.value)
    print(m.my_value)
    print(m.my_method())

if __name__ == "__main__":
    main()

属性

#[pyclass] struct

    #[pyclass]
    struct MyClass {
        #[pyo3(get, set)]
        value: String,
    }
属性 説明 Rustの実装例 Pythonの使用例
#[pyo3(get)] プロパティーとして値を取得できる。 #[pyo3(get)]
value: String,
v = obj.value
#[pyo3(set)] プロパティーとして値を設定できる。 #[pyo3(set)]
value: String,
obj.value = "abc"

#[pymethods] impl

    #[pymethods]
    impl MyClass {
〜
    }
属性 説明 Rustの実装例 Pythonの使用例
#[new] コンストラクターとして扱われる。 #[new]
fn new(value: String) -> Self {
    Self { value }
}
obj = example_pyo3.MyClass("abc")
なし 何も属性を付けないfnは、メソッドとして呼び出せる。 fn my_method(&self) -> &str {
    &self.value
}
v = obj.my_method()
#[getter] プロパティーとして値を取得する。
メソッド名の先頭の「get_」を除いた部分がプロパティー名になる。
メソッド名の先頭が「get_」でない場合はメソッド名がそのままプロパティー名になる。
#[getter]
fn get_my_value(&self) -> &str {
    &self.value
}
v = obj.my_value
#[getter]
fn my_value(&self) -> &str {
    &self.value
}
#[setter] プロパティーとして値を設定する。
メソッド名の先頭の「set_」を除いた部分がプロパティー名になる。
#[setter]
fn set_my_value(&mut self, value: String) {
    self.value = value;
}
obj.my_value = "abc"

構造体のソースファイルを分ける例

構造体を別のソースファイルで定義し、pymoduleに取り込むことが出来る。

example-pyo3/src/my_class.rs

use pyo3::prelude::*;

#[pyclass]
pub struct MyClass {
    value: String,
}

#[pymethods]
impl MyClass {
    #[new]
    fn new(value: String) -> Self {
        Self { value }
    }

    fn my_method(&self) -> &str {
        &self.value
    }
}

example-pyo3/src/lib.rs:

use pyo3::prelude::*;

mod my_class;

#[pymodule]
mod example_pyo3 {
    use pyo3::prelude::*;

    #[pymodule_export]
    use super::my_class::MyClass;
}

#[pymodule_export]で、他の場所で定義されている構造体をPyhton用にエクスポートする。

call-pyo3/main.py:

import example_pyo3

def main():
    c = example_pyo3.MyClass("abc")
    print(c.my_method())

if __name__ == "__main__":
    main()

Pythonで文字列として表示する例

オブジェクト(クラスのインスタンス)をPythonのprint文で表示できるようにするには、__str__メソッドを定義する。[2025-12-16]

example-pyo3/src/my_class.rs:

use pyo3::prelude::*;
#[pymethods]
impl MyClass {
〜
    fn __str__(&self) -> String {
        format!("pyo3__str__ {}", self.value)
    }
}

call-pyo3/main.py:

import example_pyo3

def main():
    c = example_pyo3.MyClass("abc"):
    print(c)

if __name__ == "__main__":
    main()

また、PythonのREPLでオブジェクトを生成したときに表示される文字列を変えるには、__repr__メソッドを定義する。
(__str__メソッドが定義されていない場合は__repr__メソッドが呼ばれる)

example-pyo3/src/my_class.rs:

#[pymethods]
impl MyClass {
〜
    fn __repr__(&self) -> String {
        format!("pyo3__repr__ {}", self.value)
    }
}

Python側

> cd example-pyo3
> uv run python
>>> import example_pyo3
>>> example_pyo3.MyClass("abc")
pyo3__repr__ abc

Pythonのwith文で使う例

クラスをPythonのwith文で使えるようにするには、(コンテキストマネージャーの)__enter__と__exit__メソッドを定義する。[2025-12-16]

__enter__メソッドは「with文のas節で返す値」を返すために呼ばれ、
__exit__メソッドはwith文から抜けるときに呼ばれる。

example-pyo3/src/my_class.rs:

use pyo3::prelude::*;
#[pymethods]
impl MyClass {
〜
    fn __enter__(slf: Bound<Self>) -> Bound<Self> {
        println!("pyo3__enter__");
        slf
    }

    fn __exit__(
        &mut self,
        _exc_type: Option<Bound<PyAny>>,
        _exc_value: Option<Bound<PyAny>>,
        _traceback: Option<Bound<PyAny>>,
    ) {
        println!("pyo3__exit__");
    }
}

exc_typeやexc_valueは例外の情報らしい。
例外が発生していないときはNoneになる。

call-pyo3/main.py:

import example_pyo3

def main():
    with example_pyo3.MyClass("abc") as c:
        print(c.my_method())

if __name__ == "__main__":
    main()

PyAnyからのキャスト

#[pyclass]を付けた構造体は、PyAnyからキャストすることが出来る。[2025-12-18]

example-pyo3/src/lib.rs:

#[pymodule]
mod example_pyo3 {
    use pyo3::{prelude::*, types::*};

    #[pyfunction]
    fn cast_example(arg: &Bound<PyAny>) -> PyResult<()> {
        let arg: PyRef<MyClass> = arg.extract()?;
        let arg: &MyClass = &arg;
〜
        Ok(())
    }

PyAnyから直接&MyClassへは変換できないが、一旦extractメソッドでPyRef<MyClass>に変換してから&MyClassにキャストできる。


pyo3へ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま