簡體   English   中英

如何在 PyO3 中實現 python 運算符

[英]How can I implement python operators in PyO3

我正在嘗試為我的數學庫在 Rust 中實現一個向量類。

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

但我不知道如何重載標准運算符(+、-、*、/)

我嘗試從 std::ops 實現 Add trait,但沒有運氣

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>

根據PyO3文檔,

Python 的對象模型為不同的對象行為定義了幾個協議,如序列、映射或數字協議。 PyO3 為它們中的每一個定義了單獨的特征。 要提供特定的 Python 對象行為,您需要為您的結構實現特定的特征。

重要說明,每個協議實現塊都必須用#[ 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 的答案。

我將添加此答案,以免其他人像我一樣搜索數小時。

使用@Abdul Niyas PM 提供的答案,我遇到了以下錯誤:

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

事實證明,這個神秘的錯誤消息隱藏了兩個問題。 第一個問題是__add__應該接受值而不是引用,因此我們刪除了selfVec2之前的& 這使我們能夠擺脫錯誤消息:

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})
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM