【问题标题】:How can I implement python operators in PyO3如何在 PyO3 中实现 python 运算符
【发布时间】:2020-03-31 06:12:02
【问题描述】:

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

#[pyclass]
struct Vec2d {
    #[pyo3(get, set)]
    x: f64,
    #[pyo3(get, set)]
    y: f64
}

但我不知道如何重载标准运算符(+、-、*、/)

我尝试从 std::ops 实现 Add 特征,但没有成功

impl Add for Vec2d {
    type Output = Vec2d;
    fn add(self, other: Vec2d) -> Vec2d {
        Vec2d{x: self.x + other.x, y: self.y + other.y }
    }
}

我还尝试将__add__ 方法添加到#[pymethods] 块

fn __add__(&self, other: & Vec2d) -> PyResult<Vec2d> {
    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>

【问题讨论】:

    标签: python rust pyo3


    【解决方案1】:

    根据PyO3 文档,

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

    重要提示,每个协议实现块都必须使用 #[pyproto] 属性进行注释。

    __add____sub__ 等在PyNumberProtocol Trait 中定义。

    因此您可以为您的 Vec2d 结构实现 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 })
       }
    }
    

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

    【讨论】:

      【解决方案2】:

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

      使用@Abdul Niyas P M 提供的答案,我遇到了以下错误:

      error: custom attribute panicked
        --> src/vec2.rs:49:1
         |
      49 | #[pyproto]
         | ^^^^^^^^^^
         |
         = help: message: fn arg type is not supported
      

      事实证明,这个神秘的错误信息隐藏了两个问题。 第一个问题是__add__ 应该取值而不是引用,因此我们删除了selfVec2 之前的&amp;。 这使我们可以摆脱错误消息:

      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的类型时,可以揭示第二个问题:

      // DOES NOT COMPILE
      #[pyproto]
      impl PyNumberProtocol for Vec2 {
          fn __add__(self: Vec2, other: Vec2) -> PyResult<Vec2> {
              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`
      

      这使我们得到了最终的工作解决方案(截至 2020 年 6 月):

      #[pyproto]
      impl PyNumberProtocol for Vec2 {
          fn __add__(lhs: Vec2, rhs: Vec2) -> PyResult<Vec2> {
              Ok(Vec2{x: lhs.x + rhs.x, y: lhs.y + rhs.y})
          }
      }
      

      在 Rust nightly 1.45 下成功编译,并已检查可以在 Python 中工作。

      也可以有其他类型的rhs

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

      另请注意,self 并不总是一个问题:

      #[pyproto]
      impl PyNumberProtocol for Vec2 {
          fn __neg__(self) -> PyResult<Vec2> {
              Ok(Vec3{x: -self.x, y: -self.y})
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-07
        • 1970-01-01
        • 2013-01-27
        • 1970-01-01
        • 2019-02-11
        • 1970-01-01
        • 2020-04-22
        相关资源
        最近更新 更多