PyO3手动实现带字段枚举的可行方案咨询(v0.18.1)
PyO3 0.18.1 实现带字段枚举的可行方案
你之前认为pyclass对此场景无用的观点不准确,pyclass是实现带字段枚举Python绑定的核心手段之一,以下是几种针对你给出的Prop枚举的具体实现方案:
方案1:为每个枚举变体实现pyclass + 转换逻辑
给每个Prop变体单独定义pyclass,让Rust枚举与Python对象实现双向转换,适合需要在Python中明确区分变体类型的场景。
use pyo3::{prelude::*, types::PyType}; // 为每个变体定义对应pyclass #[pyclass(name = "PropStr")] struct PyPropStr(String); #[pyclass(name = "PropI32")] struct PyPropI32(i32); #[pyclass(name = "PropI64")] struct PyPropI64(i64); #[pyclass(name = "PropU32")] struct PyPropU32(u32); #[pyclass(name = "PropU64")] struct PyPropU64(u64); #[pyclass(name = "PropF32")] struct PyPropF32(f32); #[pyclass(name = "PropF64")] struct PyPropF64(f64); #[pyclass(name = "PropBool")] struct PyPropBool(bool); // 实现Rust枚举转Python对象 impl IntoPy<PyObject> for Prop { fn into_py(self, py: Python<'_>) -> PyObject { match self { Prop::Str(s) => PyPropStr(s).into_py(py), Prop::I32(n) => PyPropI32(n).into_py(py), Prop::I64(n) => PyPropI64(n).into_py(py), Prop::U32(n) => PyPropU32(n).into_py(py), Prop::U64(n) => PyPropU64(n).into_py(py), Prop::F32(f) => PyPropF32(f).into_py(py), Prop::F64(f) => PyPropF64(f).into_py(py), Prop::Bool(b) => PyPropBool(b).into_py(py), } } } // 实现Python对象转Rust枚举 impl<'source> FromPyObject<'source> for Prop { fn extract(ob: &'source PyAny) -> PyResult<Self> { if let Ok(py_str) = ob.extract::<PyPropStr>() { Ok(Prop::Str(py_str.0)) } else if let Ok(py_i32) = ob.extract::<PyPropI32>() { Ok(Prop::I32(py_i32.0)) } else if let Ok(py_i64) = ob.extract::<PyPropI64>() { Ok(Prop::I64(py_i64.0)) } else if let Ok(py_u32) = ob.extract::<PyPropU32>() { Ok(Prop::U32(py_u32.0)) } else if let Ok(py_u64) = ob.extract::<PyPropU64>() { Ok(Prop::U64(py_u64.0)) } else if let Ok(py_f32) = ob.extract::<PyPropF32>() { Ok(Prop::F32(py_f32.0)) } else if let Ok(py_f64) = ob.extract::<PyPropF64>() { Ok(Prop::F64(py_f64.0)) } else if let Ok(py_bool) = ob.extract::<PyPropBool>() { Ok(Prop::Bool(py_bool.0)) } else { Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>( "Expected a Prop variant instance", )) } } } // 在Python模块中注册所有变体类 #[pymodule] fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::<PyPropStr>()?; m.add_class::<PyPropI32>()?; m.add_class::<PyPropI64>()?; m.add_class::<PyPropU32>()?; m.add_class::<PyPropU64>()?; m.add_class::<PyPropF32>()?; m.add_class::<PyPropF64>()?; m.add_class::<PyPropBool>()?; Ok(()) }
方案2:用单个pyclass模拟枚举类
定义统一的Prop类,内部存储Rust枚举实例,通过__new__方法根据输入值创建对应变体,适合希望在Python中用单一类型管理所有变体的场景。
use pyo3::{prelude::*, types::PyDict}; #[pyclass(name = "Prop")] struct PyProp { inner: Prop, } #[pymethods] impl PyProp { // 模拟枚举构造逻辑,自动匹配输入值类型 #[new] #[pyo3(signature = (value))] fn new(value: &PyAny) -> PyResult<Self> { let inner = if let Ok(s) = value.extract::<String>() { Prop::Str(s) } else if let Ok(n) = value.extract::<i32>() { Prop::I32(n) } else if let Ok(n) = value.extract::<i64>() { Prop::I64(n) } else if let Ok(n) = value.extract::<u32>() { Prop::U32(n) } else if let Ok(n) = value.extract::<u64>() { Prop::U64(n) } else if let Ok(f) = value.extract::<f32>() { Prop::F32(f) } else if let Ok(f) = value.extract::<f64>() { Prop::F64(f) } else if let Ok(b) = value.extract::<bool>() { Prop::Bool(b) } else { return Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>( "Unsupported value type for Prop", )); }; Ok(Self { inner }) } // 实现__repr__方便调试查看变体类型 fn __repr__(&self) -> String { match &self.inner { Prop::Str(s) => format!("Prop.Str({:?})", s), Prop::I32(n) => format!("Prop.I32({})", n), Prop::I64(n) => format!("Prop.I64({})", n), Prop::U32(n) => format!("Prop.U32({})", n), Prop::U64(n) => format!("Prop.U64({})", n), Prop::F32(f) => format!("Prop.F32({})", f), Prop::F64(f) => format!("Prop.F64({})", f), Prop::Bool(b) => format!("Prop.Bool({})", b), } } // 获取内部值的getter方法 #[getter] fn value(&self) -> PyObject { Python::with_gil(|py| match &self.inner { Prop::Str(s) => s.into_py(py), Prop::I32(n) => n.into_py(py), Prop::I64(n) => n.into_py(py), Prop::U32(n) => n.into_py(py), Prop::U64(n) => n.into_py(py), Prop::F32(f) => f.into_py(py), Prop::F64(f) => f.into_py(py), Prop::Bool(b) => b.into_py(py), }) } } // 实现Rust枚举与PyProp的互转 impl From<Prop> for PyProp { fn from(inner: Prop) -> Self { Self { inner } } } impl IntoPy<PyObject> for Prop { fn into_py(self, py: Python<'_>) -> PyObject { PyProp::from(self).into_py(py) } } impl<'source> FromPyObject<'source> for Prop { fn extract(ob: &'source PyAny) -> PyResult<Self> { let py_prop = ob.extract::<PyProp>()?; Ok(py_prop.inner) } } // 注册模块 #[pymodule] fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::<PyProp>()?; Ok(()) }
方案3:直接映射到Python原生类型(轻量方案)
如果不需要在Python中显式区分Prop变体,仅需完成双向转换,可以直接将Prop映射到Python原生类型(str/int/float/bool),无需定义pyclass。
use pyo3::{prelude::*, types::PyAny}; // Rust枚举转Python原生类型 impl IntoPy<PyObject> for Prop { fn into_py(self, py: Python<'_>) -> PyObject { match self { Prop::Str(s) => s.into_py(py), Prop::I32(n) => n.into_py(py), Prop::I64(n) => n.into_py(py), Prop::U32(n) => n.into_py(py), Prop::U64(n) => n.into_py(py), Prop::F32(f) => f.into_py(py), Prop::F64(f) => f.into_py(py), Prop::Bool(b) => b.into_py(py), } } } // Python原生类型转Rust枚举 impl<'source> FromPyObject<'source> for Prop { fn extract(ob: &'source PyAny) -> PyResult<Self> { if let Ok(s) = ob.extract::<String>() { Ok(Prop::Str(s)) } else if let Ok(n) = ob.extract::<i32>() { Ok(Prop::I32(n)) } else if let Ok(n) = ob.extract::<i64>() { Ok(Prop::I64(n)) } else if let Ok(n) = ob.extract::<u32>() { Ok(Prop::U32(n)) } else if let Ok(n) = ob.extract::<u64>() { Ok(Prop::U64(n)) } else if let Ok(f) = ob.extract::<f32>() { Ok(Prop::F32(f)) } else if let Ok(f) = ob.extract::<f64>() { Ok(Prop::F64(f)) } else if let Ok(b) = ob.extract::<bool>() { Ok(Prop::Bool(b)) } else { Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>( "Unsupported type for Prop", )) } } }
内容的提问来源于stack exchange,提问作者Shivam
相关产品推荐
相关产品推荐

