[英]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__
應該接受值而不是引用,因此我們刪除了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
的類型時,可以揭示這些問題中的第二個:
// 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.