第 33 课 · 进阶 · 18 分钟

Python 枚举

为一组固定值命名一次,然后在所有地方使用这些名字。

枚举Enum 成员查找遍历

有人在杯型栏里打了「中怀」,查价直接落空。如果一开始就只允许固定的几个名字,这个错误在门口就会被拦下。为什么 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 运行时,可能需要几秒钟。

浏览器内 Python本地运行 · 无需账号
首次运行会从 CDN 下载 Pyodide。修改后点击“运行代码”。

练习本课

12 道题,作答后立即看到解释。

查看 12 道题答案开始练习
← functoolsCSV 文件 →