You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 23:25:27