直接继承 Enum 无法做类型检查,因为 Enum 成员不是 int 或 str 的子类,mypy 不报错导致运行时类型错误;需用 IntEnum、StrEnum 或手动继承 int/str 混入 Enum 实现强类型检查。

为什么直接继承 Enum 无法做类型检查?
Python 默认的 Enum 是“值不可变但类型宽松”的——比如 Color.RED 的类型是 Color,但你把它传给期待 int 或 str 的函数时,类型检查器(如 mypy)完全不报错,因为 Enum 成员本身不是 int 或 str 的子类。这导致运行时才暴露类型错误,比如误把 Status.ACTIVE 当成整数参与计算。
用 IntEnum 或 StrEnum 显式声明底层类型
要让枚举成员具备可检查的底层类型,必须选用带语义绑定的基类:IntEnum(继承自 int)、StrEnum(3.11+,继承自 str),或手动继承 int/str 并混入 Enum。
-
IntEnum成员可直接用于数值比较、算术(但慎用:Priority.HIGH + 1语义模糊) -
StrEnum成员支持字符串方法(.upper()、.startswith()),且能被 mypy 识别为str - 若用 Python class MyStrEnum(str, Enum) 替代
StrEnum,但需确保所有值都是字符串字面量
示例:
from enum import IntEnum, StrEnum <p>class Priority(IntEnum): LOW = 1 MEDIUM = 5 HIGH = 10</p><p>class Status(StrEnum): PENDING = "pending" DONE = "done"
此时 mypy 能正确校验:def expect_int(x: int) -> None: ...; expect_int(Priority.HIGH) 通过;而 expect_int(Status.DONE) 会报错。
立即学习“Python免费学习笔记(深入)”;
避免混合类型值破坏类型一致性
一旦选定了底层类型,所有枚举成员必须严格匹配该类型,否则类型检查器可能静默失效或行为异常。
-
IntEnum中混入字符串值(如UNKNOWN = "unknown")会导致 mypy 报错,且运行时该成员不是int实例 -
StrEnum中混入数字(如ERROR = 404)同样破坏类型契约,mypy 可能漏检,但isinstance(Status.ERROR, str)为False - 枚举类中定义非成员属性(如
DEFAULT = PENDING)不会被 mypy 视为枚举成员,类型检查不覆盖它
需要序列化时注意 __members__ 和 __members__.values() 的类型
枚举类的 __members__ 是 Mapping[str, Enum],其 .values() 返回的是原始枚举成员,类型仍是 Priority 或 Status,不是 int 或 str。想获得底层值列表,得显式映射:
# 错误:type: List[Priority],不是 List[int] int_list = list(Priority.__members__.values()) <h1>正确:显式提取 int 值</h1><p>int_list = [p.value for p in Priority]</p><h1>或用 tuple(Priority) —— 返回成员实例,仍需 .value</h1><p>
如果用在 JSON 序列化中,别直接 json.dumps(list(Priority)),会失败;应 json.dumps([e.value for e in Priority]) 或为枚举实现 __json__ 方法。
强类型枚举真正起效的前提是:整个调用链都启用类型检查,且不绕过类型提示做动态操作(比如用 getattr 获取成员后直接当 int 用)。一旦出现 cast 或 Any,类型安全就断掉了。


















