有人在杯型栏里打了「中怀」,查价直接落空。如果一开始就只允许固定的几个名字,这个错误在门口就会被拦下。为什么 Enum 比裸字符串安全?
Enum 为一组固定的命名值提供真正的类型。成员是唯一对象,比到处传递裸字符串更容易比较和校验。需要显示或序列化时,还能分别取得名称和值。 你还会把 auto 值留在内部、组合权限,并校验存储值。
定义命名成员
继承 Enum,并给名字赋值。Color.RED 是成员对象,不是字符串 "red"。用 .name 取得标识符,用 .value 取得底层值。成员之间按身份比较。
from enum import Enum
class Color(Enum):
RED = "red"
GREEN = "green"
print(Color.RED.value)
print(Color.RED.name)有稳定值的名字
遍历成员
Enum 类可以按定义顺序遍历。它适合构建菜单、校验选项和生成文档。遍历产生的是成员对象,不是原始值。
from enum import Enum
class Color(Enum):
RED = "red"
GREEN = "green"
for color in Color:
print(color.name, color.value)一组固定的选择
按值或按名称查找
用值调用枚举会返回对应成员,找不到时抛出 ValueError。方括号按成员名称查找,名称不存在时抛出 KeyError。根据输入来源选择正确的查找方式。
from enum import Enum
class Color(Enum):
RED = "red"
GREEN = "green"
print(Color("green"))
print(Color["RED"])两种查找方式
auto() 的值只是实现细节
auto() 会分配连续整数,程序内部用很方便,但不适合持久化。插入新成员时这些数字可能改变。@unique 能抓住意外别名,也就是两个名字拿到同一个值的情况。
from enum import Enum, auto, unique
@unique
class Stage(Enum):
NEW = auto()
PAID = auto()
SHIPPED = auto()
print([(stage.name, stage.value) for stage in Stage])
print(Stage.PAID is Stage(2))名字稳定,auto 数字只在本程序内有效
用 Flag 组合权限
Flag 成员是可以按位组合的位值。检查权限时,用按位与再和该权限比较,不要直接比较整个组合值。只有在边界格式有明确文档时,才存储组合后的整数。
from enum import Flag, auto
class Permission(Flag):
READ = auto()
WRITE = auto()
ADMIN = READ | WRITE
editor = Permission.READ | Permission.WRITE
print(editor)
print((editor & Permission.READ) == Permission.READ)
print((editor & Permission.ADMIN) == Permission.ADMIN)小型权限集合用位标志
存值,读回时再校验
Enum 成员不是 JSON 值。在边界存 member.value,读回时调用枚举类,既校验又还原成员。未知值会抛出 ValueError,这正是我们想要的失败,而不是悄悄接受拼写错误。
from enum import Enum
class Plan(Enum):
FREE = "free"
PRO = "pro"
payload = {"plan": Plan.PRO.value}
restored = Plan(payload["plan"])
print(restored.name, restored.value)
try:
Plan("team")
except ValueError as error:
print(type(error).__name__, error)存储用值,代码里用成员
本课要点
- Enum 成员是带名称和值的唯一对象。
- 用 .name 和 .value 分别访问名称和值。
- 遍历会按定义顺序产生成员。
- Color(value) 按值查找;Color[name] 按名称查找。
- 无效查找会按方式不同抛出 ValueError 或 KeyError。
- 不要持久化 auto 值;组合状态用 Flag;读回存储值时让枚举类校验。
常见错误
- 把枚举成员和它的原始值直接比较,并期待相等。
- 需要封闭选项集合时,仍然使用裸字符串。
- 混淆按值查找和按名称查找。
- 把 Enum 成员直接写进 JSON,以为它会自动变成对应的值。
定义带 RED 和 GREEN 的 Color 枚举,打印 Color.RED.value,再查找 Color('green')。
动手试试
改改变量、先预测输出,再运行代码。第一次点击“运行代码”时会下载 Python 运行时,可能需要几秒钟。
练习本课
12 道题,作答后立即看到解释。