PyO3 - 从 PyObject 派生枚举
PyO3 - Deriving FromPyObject for Enums
我正在尝试使用 PyO3(版本:0.13.2)从 Rust 构建一个 Python 包。现在我一直在努力让枚举的转换工作。我有一个像这样的简单枚举:
#[derive(FromPyObject)]
#[derive(Copy,Clone,PartialEq,Eq)]
enum Direction {
Left,
Right,
Up,
Down
}
我按照 the documentation 添加了 #[derive(FromPyObject)]
,但是,我收到以下错误:
error: cannot derive FromPyObject for empty structs and variants -->
src/main.rs:3:10 | 3 | #[derive(FromPyObject)] |
^^^^^^^^^^^^ | = note: this error originates in a derive macro (in
Nightly builds, run with -Z macro-backtrace for more info)
在示例中,所有枚举值都具有与之关联的类型。如果这是错误的根源,是否有任何解决方法,以便它可以与我拥有的枚举一起使用?
感谢您的帮助。
解决方案
这是我最终得到的解决方案。我是 Rust 的新手,所以使用它需要您自担风险。感谢 this question 的 Ahmed Mehrez 为宏提供了基础。
您将需要以下依赖项。
[dependencies]
num-traits = "0.2"
num-derive = "0.3"
该宏为枚举实现了 IntoPy 和 FromPyObject。它将 to/from 转换为 int。另外,您现在可以迭代枚举!
use pyo3::prelude::*;
#[macro_use]
extern crate num_derive;
use num_traits::FromPrimitive;
//
macro_rules! simple_enum {
($visibility:vis, $name:ident, $($member:tt),*) => {
#[derive(Copy,Clone)]
$visibility enum $name {$($member),*}
impl $name {
fn iterate() -> Vec<$name> {
vec![$($name::$member,) *]
}
}
impl IntoPy<PyObject> for $name {
fn into_py(self, py: Python) -> PyObject {
(self as u8).into_py(py)
}
}
impl FromPyObject<'_> for $name {
fn extract(ob: &'_ PyAny) -> PyResult<$name> {
let value: u8 = ob.extract().unwrap();
if let Some(val) = FromPrimitive::from_u8(value) {
for member in $name::iterate() {
if (member as u8) == val {
return Ok(member);
}
}
}
panic!("Invalid value ({}).", value);
}
}
};
($name:ident, $($member:tt),*) => {
simple_enum!(, $name, $($member),*)
};
}
// Example
simple_enum!(pub, Direction,
Left,
Right,
Up,
Down
);
从 Python 开始,您需要重新定义您的枚举并将该值用于您的 Rust 模块。
from enum import Enum
class Direction(Enum):
Left = 1
Right = 2
Up = 3
Down = 4
// Direction.Left.value
目前没有此类枚举的派生。 FromPyObject
推导旨在处理来自 Python 端的多态输入,而不是区分单元类型。
但是,自去年夏天以来,a stale PR 在 PyO3 上添加了通用枚举支持。如果这得到一些进展,您将来可能能够处理 Python 枚举。
在那之前,您需要手动实施 FromPyObject
并决定哪些输入映射到哪个变体。
如果你想从 Python 传递一个字符串并从中获取 Rust 中匹配的枚举变体,你也可以让你的界面在 Rust 中采用 String
,添加一个 impl TryFrom<&str> for Direction
并尝试在您的接口函数中进行转换。
我正在尝试使用 PyO3(版本:0.13.2)从 Rust 构建一个 Python 包。现在我一直在努力让枚举的转换工作。我有一个像这样的简单枚举:
#[derive(FromPyObject)]
#[derive(Copy,Clone,PartialEq,Eq)]
enum Direction {
Left,
Right,
Up,
Down
}
我按照 the documentation 添加了 #[derive(FromPyObject)]
,但是,我收到以下错误:
error: cannot derive FromPyObject for empty structs and variants --> src/main.rs:3:10 | 3 | #[derive(FromPyObject)] |
^^^^^^^^^^^^ | = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
在示例中,所有枚举值都具有与之关联的类型。如果这是错误的根源,是否有任何解决方法,以便它可以与我拥有的枚举一起使用?
感谢您的帮助。
解决方案
这是我最终得到的解决方案。我是 Rust 的新手,所以使用它需要您自担风险。感谢 this question 的 Ahmed Mehrez 为宏提供了基础。
您将需要以下依赖项。
[dependencies]
num-traits = "0.2"
num-derive = "0.3"
该宏为枚举实现了 IntoPy 和 FromPyObject。它将 to/from 转换为 int。另外,您现在可以迭代枚举!
use pyo3::prelude::*;
#[macro_use]
extern crate num_derive;
use num_traits::FromPrimitive;
//
macro_rules! simple_enum {
($visibility:vis, $name:ident, $($member:tt),*) => {
#[derive(Copy,Clone)]
$visibility enum $name {$($member),*}
impl $name {
fn iterate() -> Vec<$name> {
vec![$($name::$member,) *]
}
}
impl IntoPy<PyObject> for $name {
fn into_py(self, py: Python) -> PyObject {
(self as u8).into_py(py)
}
}
impl FromPyObject<'_> for $name {
fn extract(ob: &'_ PyAny) -> PyResult<$name> {
let value: u8 = ob.extract().unwrap();
if let Some(val) = FromPrimitive::from_u8(value) {
for member in $name::iterate() {
if (member as u8) == val {
return Ok(member);
}
}
}
panic!("Invalid value ({}).", value);
}
}
};
($name:ident, $($member:tt),*) => {
simple_enum!(, $name, $($member),*)
};
}
// Example
simple_enum!(pub, Direction,
Left,
Right,
Up,
Down
);
从 Python 开始,您需要重新定义您的枚举并将该值用于您的 Rust 模块。
from enum import Enum
class Direction(Enum):
Left = 1
Right = 2
Up = 3
Down = 4
// Direction.Left.value
目前没有此类枚举的派生。 FromPyObject
推导旨在处理来自 Python 端的多态输入,而不是区分单元类型。
但是,自去年夏天以来,a stale PR 在 PyO3 上添加了通用枚举支持。如果这得到一些进展,您将来可能能够处理 Python 枚举。
在那之前,您需要手动实施 FromPyObject
并决定哪些输入映射到哪个变体。
如果你想从 Python 传递一个字符串并从中获取 Rust 中匹配的枚举变体,你也可以让你的界面在 Rust 中采用 String
,添加一个 impl TryFrom<&str> for Direction
并尝试在您的接口函数中进行转换。