微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Python中的TypeError在反映数字仿真器例如__radd__中使用PyAny作为Rust pyo3 pyclass结构

如何解决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 举报,一经查实,本站将立刻删除。