Python内置的enum模块用于把一组相关常量组织为具名成员,最直接的价值是替代状态码、类型码等“魔术数字”。原文示例中,如果代码写成 if status == 1、elif status == 2,可读性差;改用OrderStatus.PROCESSING这类枚举成员后,意图更明确,也能约束取值范围并减少拼写错误。
一、创建与访问Enum
先定义Color枚举,成员RED=1、GREEN=2、BLUE=3。枚举成员可以通过属性、名字索引和值三种方式取得:- from enum import Enum
- class Color(Enum):
- RED = 1
- GREEN = 2
- BLUE = 3
- print(Color.RED) # Color.RED
- print(Color['RED']) # Color.RED
- print(Color(1)) # Color.RED
复制代码
成员的name和value分别保存名字和值:- print(Color.RED.name) # RED
- print(Color.RED.value) # 1
- print(Color.RED == Color.RED) # True
- print(Color.RED == Color.BLUE) # False
- print(Color.RED is Color.RED) # True
- # print(Color.RED > Color.GREEN) # TypeError
复制代码
普通Enum不支持大小比较,这一点与后续IntEnum不同。枚举可以直接迭代:- for color in Color:
- print(f'{color.name} = {color.value}')
复制代码
二、auto自动赋值
当不关心中间值,只想要连续编号时,可以用auto()。默认从1开始递增:- from enum import Enum, auto
- class Status(Enum):
- PENDING = auto()
- APPROVED = auto()
- REJECTED = auto()
- CANCELLED = auto()
- print([(s.name, s.value) for s in Status])
- # [('PENDING', 1), ('APPROVED', 2), ('REJECTED', 3), ('CANCELLED', 4)]
复制代码
如果需要自定义auto生成规则,可实现_generate_next_value_。原文用成员名的小写作为值:- class MyAuto(Enum):
- def _generate_next_value_(name, start, count, last_values):
- return name.lower()
- class ConfigKey(MyAuto):
- HOST = auto()
- PORT = auto()
- DEBUG = auto()
- print([(k.name, k.value) for k in ConfigKey])
复制代码
三、IntEnum:兼容整数比较
IntEnum继承整数能力,枚举成员既可作为枚举使用,也能当整数参与比较和运算:- from enum import IntEnum
- class Priority(IntEnum):
- LOW = 1
- MEDIUM = 3
- HIGH = 5
- URGENT = 10
- print(Priority.HIGH == 5) # True
- print(Priority.HIGH > Priority.LOW) # True
- print(Priority.HIGH + 1) # 6
复制代码
注意,这种便利会牺牲一部分类型安全性。比如Priority.HIGH == 5为True,可能掩盖“把整数直接当枚举传”的意图。
四、Flag:位标志与权限组合
Flag适合表达可组合的权限、选项。auto()在Flag中按位生成1、2、4:- from enum import Flag, auto
- class Permission(Flag):
- NONE = 0
- READ = auto() # 1
- WRITE = auto() # 2
- DELETE = auto() # 4
- ADMIN = READ | WRITE | DELETE # 7
- user_perms = Permission.READ | Permission.WRITE
- print(user_perms) # Permission.READ|WRITE
- print(Permission.READ in user_perms) # True
- print(Permission.DELETE in user_perms) # False
- user_perms |= Permission.DELETE
- print(Permission.ADMIN in user_perms) # True
复制代码
权限检查可以封装为函数,用“所需权限是否包含在用户权限中”判断:- def can_access(required, user_permission):
- return required in user_permission
- print(can_access(Permission.READ, Permission.READ | Permission.WRITE))
- print(can_access(Permission.DELETE, Permission.READ | Permission.WRITE))
复制代码
五、实战:HTTP状态码枚举
IntEnum很适合包装HTTP状态码。下面枚举包含200、201、400、401、403、404、500,并提供了判断成功和客户端错误的方法:- from enum import IntEnum
- class HttpStatus(IntEnum):
- OK = 200
- CREATED = 201
- BAD_REQUEST = 400
- UNAUTHORIZED = 401
- FORBIDDEN = 403
- NOT_FOUND = 404
- INTERNAL_ERROR = 500
- def is_success(self):
- return 200 <= self.value < 300
- def is_client_error(self):
- return 400 <= self.value < 500
- def handle_response(status_code, body):
- status = HttpStatus(status_code)
- if status.is_success():
- print(f'✓ {status.name}: {body}')
- elif status.is_client_error():
- print(f'✗ {status.name}: 客户端错误')
- else:
- print(f'⚠ {status.name}: 服务器错误')
- handle_response(200, '操作成功')
- handle_response(404, None)
复制代码
六、实战:配置日志级别
普通Enum默认不支持>=比较,但可以通过实现__ge__来支持。原文示例将DEBUG、INFO、WARNING、ERROR分别设为10、20、30、40:- from enum import Enum
- class LogLevel(Enum):
- DEBUG = 10
- INFO = 20
- WARNING = 30
- ERROR = 40
- def __ge__(self, other):
- if self.__class__ is other.__class__:
- return self.value >= other.value
- return NotImplemented
- class LogConfig:
- current_level = LogLevel.INFO
- @classmethod
- def set_level(cls, level):
- if not isinstance(level, LogLevel):
- raise TypeError(f'请使用LogLevel枚举,而不是 {type(level)}')
- cls.current_level = level
- @classmethod
- def should_log(cls, level):
- return level >= cls.current_level
- LogConfig.set_level(LogLevel.DEBUG)
- print(LogConfig.should_log(LogLevel.INFO)) # True
复制代码
七、选型与使用建议
通用枚举用Enum,重点是类型安全和名字清晰,不应与整数混用;需要兼容整数比较或运算时用IntEnum;需要位运算组合时用Flag;不关心具体值、只需自动编号时用auto()。成员命名通常采用全大写,类名采用PascalCase。代码中一旦出现0、1、2这类状态码,优先考虑用枚举表达。 |