如何解决Python中的TypeError在反映数字仿真器例如__radd__中使用PyAny作为Rust pyo3 pyclass结构
我使用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)。但这显然不允许任何其他类型(浮点数,列表,类等)。我找不到任何有效的通用类型,也找不到任何重载__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,而是因为它没有FiniteSet
!我添加了一个简单的__add__
方法,而且确定会破坏它。
无论如何,从github讨论中的聊天中看来,这将在pyo3 0.12中正常工作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。