小白学 Python
AI 编程指南GitHub
© 2026 小白学 Python · 基于 walter201230/Python 教程
课程目录关于本站联系方式隐私政策GitHub

枚举类

完整讲解 · 5 段教学·配 4 道练习题·预计 30 分钟

本页是本章的通读版,可直接读完全部讲解。想动手写代码、跑判分,去 闯关模式。

教学 01 / 05

第十节:枚举类

这一节学的是 枚举类(Enum)——一种用来 代替魔法常量 的语法工具。

本节脉络

  • 一、为什么要用枚举(取代散落的常量)
  • 二、Enum 基本用法
  • 三、自定义类型 / @unique / auto()
  • 四、枚举的比较:== / is / IntEnum
教学 02 / 05

一、为什么要用枚举

实际开发里,我们经常会遇到「几个固定的、有限的取值」这种场景:

  • 月份:1~12
  • 星期:周一~周日
  • 订单状态:待支付 / 已支付 / 已发货 / 已完成 / 已取消
  • 颜色:红 / 绿 / 蓝

最朴素的写法是用一组大写常量:

python到闯关页运行这段 →
JAN = 1
FEB = 2
MAR = 3
# ...
NOV = 11
DEC = 12

简单快捷,但有三个缺点:

  1. 类型太松——JAN 只是一个 int,谁都可以拿别的整数来冒充:def show_month(m),调用方传进来 m = 99,函数也没办法知道这是不是一个合法的月份。
  2. 散落各处——常量分散在文件里,没有「这一组属于一类」的语义。
  3. 可被修改——它就是变量,JAN = 100 也不会报错。

更好的做法是 定义一个 class,每个常量都是 class 里唯一的实例。

Python 标准库的 enum 模块就是干这个的。看看效果:

python到闯关页运行这段 →
from enum import Enum

class Month(Enum):
    JAN = 1
    FEB = 2
    MAR = 3
    # ...
    DEC = 12

print(Month.JAN)        # Month.JAN
print(Month.JAN.name)   # 'JAN'
print(Month.JAN.value)  # 1

跟普通常量比,Enum 的好处:

  • 类型清晰:Month.JAN 的类型就是 Month,传错参数立刻看出来
  • 不可修改:Month.JAN = 999 会直接报错
  • 不可实例化:Month() 不会创造新成员,所有成员都是 单例
  • 可迭代:for m in Month: ... 直接拿到所有成员

Enum 的成员均为 单例(Singleton),并且 不可实例化、不可更改。

教学 03 / 05

二、Enum 基本用法

1、定义一个 Enum

最常见的写法是 继承 Enum 类:

python到闯关页运行这段 →
from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Color 本身是一个 类,Color.RED / Color.GREEN / Color.BLUE 是它的三个 成员。

2、访问成员

python到闯关页运行这段 →
print(Color.RED)         # Color.RED        ← 默认 repr
print(Color.RED.name)    # 'RED'            ← 成员名
print(Color.RED.value)   # 1                ← 对应的值

记住两个属性:

  • .name —— 成员的 名字(字符串)
  • .value —— 成员的 值(任意类型)

3、遍历所有成员

Enum 是可迭代的:

python到闯关页运行这段 →
for c in Color:
    print(c.name, c.value)

输出:

RED 1
GREEN 2
BLUE 3

也可以用 __members__ 拿到一个 有序映射(包含别名):

python到闯关页运行这段 →
for name, member in Color.__members__.items():
    print(name, '->', member, '->', member.value)

4、另一种创建方式(函数式)

不写 class,直接用 Enum(...) 函数:

python到闯关页运行这段 →
from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                       'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

print(Month.Jan)        # Month.Jan
print(Month.Jan.value)  # 1,从 1 开始自动分配

第一个参数是类名,第二个参数是成员名的可迭代对象。这种写法适合「成员只是一组名字、不在乎具体的值」的场景。

推荐 优先用 class 写法——明确、可读、IDE 提示更友好。函数式写法只在需要动态生成 Enum 时才用。

练习 1 / 4定义一个 Color 枚举类去闯关页做这题 →练习 2 / 4访问 .name 和 .value去闯关页做这题 →练习 3 / 4遍历 Enum 的所有成员去闯关页做这题 →
教学 04 / 05

三、自定义类型、@unique、auto()

1、值可以是任意类型

Enum 成员的值不一定要是数字,字符串、元组、对象都行:

python到闯关页运行这段 →
from enum import Enum

class Month(Enum):
    Jan = 'January'
    Feb = 'February'
    Mar = 'March'
    # ...
    Dec = 'December'

print(Month.Jan)         # Month.Jan
print(Month.Jan.name)    # 'Jan'
print(Month.Jan.value)   # 'January'

2、@unique:禁止重复值

默认情况下,Enum 允许 多个成员有相同的值——后定义的会变成前面的别名。

python到闯关页运行这段 →
from enum import Enum

class Color(Enum):
    RED = 1
    SCARLET = 1   # 不报错,SCARLET 成了 RED 的别名

print(Color.SCARLET is Color.RED)   # True

如果你不想这样,加上 @unique 装饰器,就强制每个值唯一,重复时直接抛错:

python到闯关页运行这段 →
from enum import Enum, unique

@unique
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
    # SCARLET = 1   ← 加上这一行就会 ValueError

3、auto():让 Python 自动给值

成员太多、又不在乎具体的值是几?用 auto() 让 Python 自动分配:

python到闯关页运行这段 →
from enum import Enum, auto

class Weekday(Enum):
    MON = auto()
    TUE = auto()
    WED = auto()
    THU = auto()
    FRI = auto()
    SAT = auto()
    SUN = auto()

print(Weekday.MON.value)   # 1
print(Weekday.SUN.value)   # 7

auto() 默认从 1 开始递增。需要自定义起始值或步长,可以重写 _generate_next_value_ 方法(进阶用法,先跳过)。

4、IntEnum / StrEnum

如果希望 Enum 成员 同时是 int(可以参与算术运算、可以和 int 比大小),用 IntEnum:

python到闯关页运行这段 →
from enum import IntEnum

class Priority(IntEnum):
    LOW = 1
    MID = 5
    HIGH = 10

print(Priority.LOW < Priority.HIGH)   # True
print(Priority.MID + 1)               # 6

StrEnum(Python 3.11+)是同理的字符串版本。

练习 4 / 4用 auto() 自动分配值去闯关页做这题 →
教学 05 / 05

四、枚举的比较

1、== 和 is

普通 Enum 成员既支持 ==(值相等比较)也支持 is(同一对象)。因为 每个成员都是单例,两者结果一致:

python到闯关页运行这段 →
from enum import Enum

class User(Enum):
    Alice = 98
    Bob = 30
    Tom = 12

a = User.Alice
b = User.Bob

print(a == b, a == User.Alice)   # False True
print(a is b, a is User.Alice)   # False True

2、不支持 < >

普通 Enum 不支持大小比较——成员之间没有「天然顺序」:

python到闯关页运行这段 →
from enum import Enum

class User(Enum):
    Alice = 98
    Bob = 30

try:
    print(User.Alice < User.Bob)
except TypeError as e:
    print('Error:', e)

输出:

Error: '<' not supported between instances of 'User' and 'User'

如果对它们 sorted(...),也会因为同一个原因抛 TypeError。

3、想比较大小?用 IntEnum

如果业务上需要比较顺序(比如优先级),用 IntEnum 替代 Enum:

python到闯关页运行这段 →
from enum import IntEnum

class User(IntEnum):
    Alice = 98
    Bob = 30
    Tom = 12

# 按 value 排序
for u in sorted(User):
    print(u.name)

输出:

Tom
Bob
Alice

IntEnum 的成员同时也是 int,所以可以用 < / > / sorted 等等。

4、什么时候用枚举?

总结一下:当你写下任何一段「这个变量只能取下面这几个值」的代码时,就应该考虑 Enum。

  • ✓ 状态机的状态:Pending / Running / Done / Failed
  • ✓ 类别 / 枚举类型:颜色、性别、文件类型
  • ✓ 优先级 / 等级:用 IntEnum,可以排序和比较

代替散落的 STATUS_PENDING = 0 这种「字符串/整数魔法值」,让代码更安全、更清晰。

读完了?动手练一遍才算真会。

去闯关模式练习 →