如何用类Enum的结构生成类型而非值?兼容类型提示与泛型
实现兼容泛型与类型提示的元枚举结构
你需要的是一种元枚举——既保留Enum的简洁定义、字符串关联、反向查找等特性,又让每个成员本身是可用于泛型的类型。以下是符合所有需求的最优实现:
完整代码实现
from typing import Generic, TypeVar, Type # 所有维度类型的基类,用于泛型约束 class DimensionBase: symbol: str # 自定义元类,复刻Enum核心特性并生成类型成员 class DimensionMeta(type): _symbol_to_type: dict[str, Type[DimensionBase]] = {} _name_to_type: dict[str, Type[DimensionBase]] = {} def __new__(cls, name, bases, namespace, symbol=None): # 创建维度类型 dim_cls = super().__new__(cls, name, bases, namespace) if symbol is not None: # 为有效维度类型绑定symbol,并注册到映射表 dim_cls.symbol = symbol cls._symbol_to_type[symbol] = dim_cls cls._name_to_type[name] = dim_cls # 将维度类型挂载到元类实例(即Dim类)上,模拟Enum的Dim.TIME调用 setattr(cls, name, dim_cls) return dim_cls def __call__(cls, symbol: str) -> Type[DimensionBase]: # 实现Enum的反向查找:Dim('t') -> TIME类型 if symbol in cls._symbol_to_type: return cls._symbol_to_type[symbol] raise ValueError(f"Unknown dimension symbol: '{symbol}'") # 维度容器类,使用自定义元类 class Dim(metaclass=DimensionMeta): pass # 定义具体维度,语法简洁度接近Enum class TIME(Dim, symbol="t"): pass class MASS(Dim, symbol="m"): pass # 测试泛型与类型提示 T = TypeVar("T", bound=DimensionBase) class PhysicalQuantity(Generic[T]): def __init__(self, value: float): self.value = value class Container: some_time: PhysicalQuantity[TIME] # 类型提示完全生效,符合直觉 some_mass: PhysicalQuantity[MASS] # 验证Enum特性 print(Dim.TIME) # <class '__main__.TIME'> print(Dim('t')) # <class '__main__.TIME'> print(TIME.symbol) # 't'
方案优势说明
- 定义简洁:只需继承
Dim并指定symbol参数,和Enum的TIME = "t"写法几乎一致,无需额外冗余代码。 - 类型系统完全兼容:
TIME/MASS是DimensionBase的子类,可直接作为泛型参数使用,类型检查器(如mypy)能正常识别PhysicalQuantity[TIME]的约束。 - 完整复刻Enum核心特性:
- 通过
Dim.TIME直接访问维度类型; - 通过
Dim('t')实现反向查找; - 每个维度类型自带
symbol属性,轻松关联字符串值。
- 通过
- 逻辑清晰:所有维度类型统一在
Dim命名空间下,结构和Enum完全一致,符合开发者使用习惯。
对比现有方案的改进
- 比
Literal方案更直观:无需每次写Literal[Dim.TIME],直接用维度类型作为泛型参数,符合用户对Dim.TIME作为类型的直觉预期。 - 比单纯类继承方案更贴近Enum行为:
Dim作为元类,实现了Dim.TIME的调用方式,而非分散的独立类。 - 比普通元类方案更完整:新增了Enum的反向查找特性,保留了Enum的所有核心使用体验。
内容的提问来源于stack exchange,提问作者TrakJohnson
相关产品推荐
相关产品推荐

