PyO3-从枚举的PyObject派生



我正试图使用PyO3从Rust构建一个Python包(版本:0.13.2(。现在我正努力让转换为enum。我有一个类似的简单枚举:

#[derive(FromPyObject)]
#[derive(Copy,Clone,PartialEq,Eq)]
enum Direction {
Left,
Right,
Up,
Down
}

我根据文档添加了#[derive(FromPyObject)],但是,我得到了以下错误:

错误:无法为空结构和变体派生FromPyObject-->src/main.rs:3:10|3|#[drive(FromPyObject(]|
夜间构建,使用-Z宏回溯运行以获取更多信息(

在示例中,所有枚举值都具有与其关联的类型。如果这是错误的来源,有没有办法绕过它,使它能够与我的枚举一起工作?

谢谢你的帮助。

解决方案

这是我最终得到的解决方案。我是Rust的新手,所以使用它的风险自负。感谢Ahmed Mehrez从这个问题为宏观提供了基础。

您将需要以下依赖项。

[dependencies]
num-traits = "0.2"
num-derive = "0.3"

宏为枚举实现IntoPy和FromPyObject。它转换为int。此外,您现在可以迭代枚举了!

use pyo3::prelude::*;
#[macro_use]
extern crate num_derive;
use num_traits::FromPrimitive;
// https://stackoverflow.com/questions/21371534/in-rust-is-there-a-way-to-iterate-through-the-values-of-an-enum
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端的多态输入,而不是区分单元类型。

然而,自去年夏天以来,一直有一个过时的PR在PyO3上添加通用枚举支持。如果这得到了一些进展,那么您将来可能能够处理Python枚举。

在此之前,您需要手动实现FromPyObject,并决定哪些输入映射到哪个变体。

如果你想从Python中传递一个字符串,并从中获得Rust中匹配的枚举变体,你也可以让你的接口在Rust中使用String,添加impl TryFrom<&str> for Direction,然后在接口函数中尝试转换。

最新更新