Python中的TypeError在Rust pyo3 pyclass结构的反射数字模拟器(例如__radd__)中使用



我已经使用pyo3为python创建了一个Rust库。它包含一个pyclass结构,该结构实现了几个PyNumberProtocol方法,如__add____sub__等…以允许+和-等python运算符处理该类。我使用PyAny作为其中大多数的"其他"对象,因为我想支持许多不同的类型。这很好,但当我尝试实现像__radd____rsub__这样的反射方法时,python会抛出一个TypeError。抛出的TypeError没有参数或消息,它只是一个空的TypeError。如果我调用myitem.__radd__(other),但other + myitem失败,则方法本身可以工作。以i64为例,我去掉了除__add____radd__之外的所有内容(下面的TestClass1(。

我可以实现特定类型的反射方法,例如i64(请参阅下面的TestClass2(。但很明显,这不允许任何不同的类型(float、list、classes等(。我找不到任何可以工作的泛型类型,也找不到重载__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示例中,除了最后一行:之外,所有打印语句都按预期工作

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

经过进一步挖掘,它似乎是pyo3当前版本的限制:https://github.com/PyO3/pyo3/issues/844

而且它与PyAny无关,我的测试太简单了。TestClass2不起作用,因为它使用了i64而不是&PyAny,但因为它没有__add__!我添加了一个简单的__add__方法,果然打破了它

无论如何,从github讨论中的讨论来看,这似乎将在平壤0.12发挥作用。

相关内容

最新更新