2

Pyo3 を使用して Python 用の Rust ライブラリを作成しました。__add__これには、 、 などのいくつかの PyNumberProtocol メソッドを実装する pyclass 構造体が含まれており、__sub__+ や - などの python 演算子をクラスで動作させることができます。多くの異なるタイプをサポートしたいので、これらのほとんどで「その他」のオブジェクトとして PyAny を使用しています。それは正常に機能していますが、 や などの反映されたメソッドを実装しようとする__radd____rsub__、python が TypeError をスローします。スローされた TypeError には引数もメッセージもありません。これは単なる空の TypeError です。呼び出してもメソッド自体は機能しますmyitem.__radd__(other)が、other + myitem失敗します。例として i64 を除いてすべてを取り除きました (以下の TestClass1 __add__) 。__radd__

i64 など、特定のタイプの反映されたメソッドを実装できます (以下の TestClass2 を参照)。しかし明らかに、これは異なるタイプ (フロート、リスト、クラスなど) を許可しません。機能するジェネリック型も、メソッドをオーバーロードする方法も見つかりませんでした__radd__。私の質問は、__radd__Python から複数の型を受け入れるように実装する方法はありますか? 私はRustにかなり慣れていないので、おそらく何か明らかなことを見逃しています...

Rust サンプル ライブラリ:

use pyo3::exceptions::TypeError;
use pyo3::prelude::*;
use pyo3::PyNumberProtocol;

macro_rules! create_test_class {
    ($name: ident) => {
        #[pyclass]
        #[derive(PartialEq, Debug, Clone)]
        pub struct $name {
            #[pyo3(get, set)]
            value: i64,
        }
        #[pymethods]
        impl $name {
            #[new]
            pub fn from_value(value: i64) -> $name {
                $name { value: value }
            }
        }
    };
}

create_test_class!(TestClass1);
create_test_class!(TestClass2);

#[pyproto]
impl PyNumberProtocol for TestClass1 {
    fn __add__(lhs: TestClass1, rhs: &PyAny) -> PyResult<TestClass1> {
        let pynum_result: Result<i64, _> = rhs.extract();
        if let Ok(pynum) = pynum_result {
            Ok(TestClass1 {
                value: lhs.value + pynum,
            })
        } else {
            Err(TypeError::py_err("Not implemented for this type!"))
        }
    }
    fn __radd__(self, other: &PyAny) -> PyResult<TestClass1> {
        let pynum_result: Result<i64, _> = other.extract();
        if let Ok(pynum) = pynum_result {
            Ok(TestClass1 {
                value: self.value + pynum,
            })
        } else {
            Err(TypeError::py_err("Not implemented for this type!"))
        }
    }
}

#[pyproto]
impl PyNumberProtocol for TestClass2 {
    fn __radd__(self, other: i64) -> PyResult<TestClass2> {
        Ok(TestClass2 {
            value: self.value + other,
        })
    }
}

#[pymodule]
fn test_class(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<TestClass1>()?;
    m.add_class::<TestClass2>()?;
    Ok(())
}

Python の例では、最後の行を除いて、すべての print ステートメントが期待どおりに機能します。

from test_class import TestClass1, TestClass2

tc2 = TestClass2(10)
print(tc2.__radd__(3).value)  # 13
print((3 + tc2).value)        # 13
try:
    3.0 + tc2                 # expected TypeError
except TypeError as e:
    print(repr(e))            # TypeError("'float' object cannot be interpreted as an integer")

tc1 = TestClass1(10)
print((tc1 + 3).value)        # 13
print(tc1.__radd__(3).value)  # 13
print((3 + tc1).value)        # unexpected, empty TypeError 

Rust 1.45.2、pyo3 0.11.1、python 3.7.3 を使用しています

4

1 に答える 1