如何在PyO3中实现python操作符

如何在PyO3中实现python操作符,python,rust,pyo3,Python,Rust,Pyo3,我正在尝试在rust中为我的数学库实现一个向量类 #[pyclass] struct Vec2d { #[pyo3(get, set)] x: f64, #[pyo3(get, set)] y: f64 } 但我不知道如何重载标准运算符(+、-、*、/) 我尝试从std::ops实现addtrait,但没有成功 impl Add for Vec2d { type Output = Vec2d; fn add(self, other: Vec2d)

我正在尝试在rust中为我的数学库实现一个向量类

#[pyclass]
struct Vec2d {
    #[pyo3(get, set)]
    x: f64,
    #[pyo3(get, set)]
    y: f64
}
但我不知道如何重载标准运算符(+、-、*、/)

我尝试从std::ops实现addtrait,但没有成功

impl Add for Vec2d {
    type Output = Vec2d;
    fn add(self, other: Vec2d) -> Vec2d {
        Vec2d{x: self.x + other.x, y: self.y + other.y }
    }
}
我还尝试向#[pymethods]块添加
\uuuuuuuuuuuuuuuuuuuuuuuu
方法

fn __add__(&self, other: & Vec2d) -> PyResult<Vec2d> {
    Ok(Vec2d{x: self.x + other.x, y: self.y + other.y })
}
fn\uuuu添加(自我、其他:&Vec2d)->PyResult{
Ok(Vec2d{x:self.x+other.x,y:self.y+other.y})
}
但仍然不起作用

对于第二种方法,我可以看到该方法存在,但python不认为它是运算符重载

In [2]: v1 = Vec2d(3, 4)
In [3]: v2 = Vec2d(6, 7)
In [4]: v1 + v2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-08104d7e1232> in <module>()
----> 1 v1 + v2

TypeError: unsupported operand type(s) for +: 'Vec2d' and 'Vec2d'

In [5]: v1.__add__(v2)
Out[5]: <Vec2d object at 0x0000026B74C2B6F0>
[2]中的
v1=Vec2d(3,4)
[3]中:v2=Vec2d(6,7)
In[4]:v1+v2
---------------------------------------------------------------------------
TypeError回溯(最近一次调用上次)
在()
---->1 v1+v2
TypeError:不支持+:“Vec2d”和“Vec2d”的操作数类型
在[5]中:v1.\uuuuu添加\uuuuuu(v2)
出[5]:
根据文件

Python的对象模型为不同的对象行为定义了几个协议,如序列、映射或数字协议。PyO3为每一个定义了单独的特性要提供特定的python对象行为,您需要实现结构的特定特性。

重要注意事项:每个协议实现块都必须使用#[
pyproto
]
属性进行注释

Trait中定义了
\uuuuuu add\uuuuuuu
\uuuuuu sub\uuuuuuu

因此,您可以为
Vec2d
struct实现
PyNumberProtocol
,以重载标准操作

#[pyproto]
impl PyNumberProtocol for Vec2d {
    fn __add__(&self, other: & Vec2d) -> PyResult<Vec2d> {
            Ok(Vec2d{x: self.x + other.x, y: self.y + other.y })
   }
}
#[pyproto]
用于Vec2d的impl PyNumberProtocol{
fn\uuu添加\uuuu(&self,其他:&Vec2d)->PyResult{
Ok(Vec2d{x:self.x+other.x,y:self.y+other.y})
}
}

此解决方案未经测试,请检查@Neven V的完整工作解决方案答案。

我将添加此答案,以避免其他人像我一样搜索数小时

使用@Abdul Niyas p M提供的答案,我有以下错误:

error: custom attribute panicked
  --> src/vec2.rs:49:1
   |
49 | #[pyproto]
   | ^^^^^^^^^^
   |
   = help: message: fn arg type is not supported
事实证明,这个神秘的错误消息隐藏了两个问题。 第一个问题是
\uuuu add\uuuu
应该取值而不是引用,因此我们删除了
self
Vec2
之前的
&
。 这使我们能够消除错误消息:

error[E0277]: the trait bound `&vec2::Vec2: pyo3::pyclass::PyClass` is not satisfied
   --> src/vec2.rs:47:1
    |
47  | #[pyproto]
    | ^^^^^^^^^^ the trait `pyo3::pyclass::PyClass` is not implemented for `&vec2::Vec2`
    | 
当我们指定
self
的类型时,可以发现第二个问题:

//不编译
#[pyproto]
Vec2的impl PyNumberProtocol{
fn\uuu添加(self:Vec2,other:Vec2)->PyResult{
Ok(Vec2{x:self.x+other.x,y:self.y+other.y})
}
}
无法使用错误消息进行编译

error[E0185]: method `__add__` has a `self: <external::vec3::Vec3 as pyo3::class::number::PyNumberAddProtocol<'p>>::Left` declaration in the impl, but not in the trait
  --> src/external/vec2.rs:49:5
   |
49 |     fn __add__(self: Vec2, other: Vec2) -> PyResult<Vec2> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self: <vec2::Vec2 as pyo3::class::number::PyNumberAddProtocol<'p>>::Left` used in impl
   |
   = note: `__add__` from trait: `fn(<Self as pyo3::class::number::PyNumberAddProtocol<'p>>::Left, <Self as pyo3::class::number::PyNumberAddProtocol<'p>>::Right) -> <Self as pyo3::class::number::PyNumberAddProtocol<'p>>::Result`
还要注意的是,拥有
self
并不总是一个问题:

#[pyproto]
Vec2的impl PyNumberProtocol{
fn\uuuu neg\uuuuuu(自)->PyResult{
Ok(Vec3{x:-self.x,y:-self.y})
}
}
#[pyproto]
impl PyNumberProtocol for Vec2 {
    fn __mul__(lhs: Vec2, rhs: f64) -> PyResult<Vec2> {
        Ok(Vec3{x: lhs.x * rhs, y: lhs.y * rhs})
    }
}