← 返回 Day 44 正文

Day 44 · 图解

Day 44:枚举与常量 — 图解


1. 枚举底层实现原理

┌─────────────────────────────────────────────────────────────────┐
│                    Enum 底层实现 (EnumMeta 元类)                  │
└─────────────────────────────────────────────────────────────────┘

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

│
▼ EnumMeta.__new__(mcs, name, bases, classdict)

┌──────────────────────────────────────────────────────────────┐
│ 1. 收集所有成员定义                                           │
│    classdict = { RED: 1, GREEN: 2, BLUE: 3 }                │
│                                                              │
│ 2. 移除非枚举成员(方法、描述符等保留)                         │
│                                                              │
│ 3. 为每个成员创建实例(单例)                                   │
│    Color.RED   = Color.__new__(Color, 1); __init__("RED")    │
│    Color.GREEN = Color.__new__(Color, 2); __init__("GREEN")  │
│    Color.BLUE  = Color.__new__(Color, 3); __init__("BLUE")   │
│                                                              │
│ 4. 将原始类属性替换为枚举成员实例                               │
│    Color.RED   → <Color.RED: 1>  (单例)                       │
│    Color.GREEN → <Color.GREEN: 2> (单例)                      │
│    Color.BLUE  → <Color.BLUE: 3>  (单例)                      │
│                                                              │
│ 5. 构建反向查找映射                                            │
│    _value2member_map_ = {1: Color.RED, 2: Color.GREEN,       │
│                          3: Color.BLUE}                       │
│                                                              │
│ 6. 冻结枚举类,禁止修改成员                                    │
└──────────────────────────────────────────────────────────────┘

2. 枚举基类选择

需要哪种枚举?
      │
      ├── 基本命名常量集 ──────────► Enum(最常用)
      │
      ├── 需要与整数兼容 ──────────► IntEnum
      │    (HTTP 状态码、协议字段)
      │
      ├── 需要字符串兼容 ──────────► StrEnum(Python 3.11+)
      │    (配置键、JSON 字段名)
      │
      └── 需要位标志运算 ──────────► IntFlag
           (权限组合、选项标志)

3. 枚举成员查找方式

三种查找方式:

  按名称查找          按值查找            按成员查找
  Color["RED"]        Color(1)            Color(Color.RED)
       │                  │                    │
       ▼                  ▼                    ▼
    _member_names_    _value2member_map_      返回自身
    [ "RED",            { 1: Color.RED,
      "GREEN",           2: Color.GREEN,
      "BLUE" ]           3: Color.BLUE  }

4. auto() 赋值机制

auto() 内部计数器(全局):

  _auto_number_incrementer = 0

  每次调用 auto():
      _auto_number_incrementer += 1
      return _auto_number_incrementer

  所以:
      class Color(Enum):
          RED = auto()    # → 1
          GREEN = auto()  # → 2
          BLUE = auto()   # → 3

  注意:不同枚举类的计数器是独立的!

5. 订单状态机流转图

          ┌──────────────────┐
          │    PENDING       │
          │    ⏳ 待处理      │
          └────────┬─────────┘
                   │
          ┌────────┴────────┐
          ▼                 ▼
  ┌───────────────┐  ┌───────────────┐
  │  PROCESSING   │  │  CANCELLED    │
  │  🔄 处理中     │  │  ❌ 已取消    │
  └───────┬───────┘  └───────────────┘
          │                    ▲
   ┌──────┴──────┐             │
   ▼             ▼             │
┌─────────┐ ┌──────────┐      │
│ SHIPPED │ │CANCELLED │      │
│ 📦 已发货│ │ ❌ 已取消 │──────┘
└────┬────┘ └──────────┘
     │
     ▼
┌───────────┐
│ DELIVERED │
│ ✅ 已签收  │
└───────────┘

终态:DELIVERED, CANCELLED(不可再转换)

6. @unique 装饰器行为

不加 @unique:                   加 @unique:

class Color(Enum):             @unique
    RED = 1                    class Color(Enum):
    CRIMSON = 1   ← 别名          RED = 1
    GREEN = 2                   CRIMSON = 1   ← ValueError!
    BLUE = 3                    GREEN = 2
                                BLUE = 3

Color(1) → Color.RED           Color(1) → 编译失败
Color.CRIMSON is Color.RED     @unique 防止了别名定义
  → True

7. 枚举 vs 常量的决策树

需要一组命名的关联值?
      │
      ├── < 3 个值,无关联逻辑 ────► 普通模块常量
      │       STATUS_OK = 200
      │
      ├── 3+ 个值,同语义 ────────► Enum 🌟
      │       class Status(Enum):
      │           OK = 200
      │           NOT_FOUND = 404
      │
      ├── 需要整数运算兼容 ───────► IntEnum
      │       class Code(IntEnum):
      │           OK = 200
      │
      ├── 需要位运算组合 ────────► IntFlag
      │       class Perm(IntFlag):
      │           READ = 4
      │
      └── 需要状态机逻辑 ────────► Enum + 转移表
              class OrderStatus(Enum):
                  PENDING = auto()
                  ...