查看: 198|回复: 0

Python enum枚举消除魔术数字与IntEnum Flag

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
Python内置的enum模块用于把一组相关常量组织为具名成员,最直接的价值是替代状态码、类型码等“魔术数字”。原文示例中,如果代码写成 if status == 1、elif status == 2,可读性差;改用OrderStatus.PROCESSING这类枚举成员后,意图更明确,也能约束取值范围并减少拼写错误。

一、创建与访问Enum
先定义Color枚举,成员RED=1、GREEN=2、BLUE=3。枚举成员可以通过属性、名字索引和值三种方式取得:
  1. from enum import Enum
  2. class Color(Enum):
  3.     RED = 1
  4.     GREEN = 2
  5.     BLUE = 3
  6. print(Color.RED)        # Color.RED
  7. print(Color['RED'])     # Color.RED
  8. print(Color(1))         # Color.RED
复制代码

成员的name和value分别保存名字和值:
  1. print(Color.RED.name)   # RED
  2. print(Color.RED.value)  # 1
  3. print(Color.RED == Color.RED)  # True
  4. print(Color.RED == Color.BLUE) # False
  5. print(Color.RED is Color.RED)  # True
  6. # print(Color.RED > Color.GREEN) # TypeError
复制代码

普通Enum不支持大小比较,这一点与后续IntEnum不同。枚举可以直接迭代:
  1. for color in Color:
  2.     print(f'{color.name} = {color.value}')
复制代码

二、auto自动赋值
当不关心中间值,只想要连续编号时,可以用auto()。默认从1开始递增:
  1. from enum import Enum, auto
  2. class Status(Enum):
  3.     PENDING = auto()
  4.     APPROVED = auto()
  5.     REJECTED = auto()
  6.     CANCELLED = auto()
  7. print([(s.name, s.value) for s in Status])
  8. # [('PENDING', 1), ('APPROVED', 2), ('REJECTED', 3), ('CANCELLED', 4)]
复制代码

如果需要自定义auto生成规则,可实现_generate_next_value_。原文用成员名的小写作为值:
  1. class MyAuto(Enum):
  2.     def _generate_next_value_(name, start, count, last_values):
  3.         return name.lower()
  4. class ConfigKey(MyAuto):
  5.     HOST = auto()
  6.     PORT = auto()
  7.     DEBUG = auto()
  8. print([(k.name, k.value) for k in ConfigKey])
复制代码

三、IntEnum:兼容整数比较
IntEnum继承整数能力,枚举成员既可作为枚举使用,也能当整数参与比较和运算:
  1. from enum import IntEnum
  2. class Priority(IntEnum):
  3.     LOW = 1
  4.     MEDIUM = 3
  5.     HIGH = 5
  6.     URGENT = 10
  7. print(Priority.HIGH == 5)          # True
  8. print(Priority.HIGH > Priority.LOW) # True
  9. print(Priority.HIGH + 1)           # 6
复制代码

注意,这种便利会牺牲一部分类型安全性。比如Priority.HIGH == 5为True,可能掩盖“把整数直接当枚举传”的意图。

四、Flag:位标志与权限组合
Flag适合表达可组合的权限、选项。auto()在Flag中按位生成1、2、4:
  1. from enum import Flag, auto
  2. class Permission(Flag):
  3.     NONE = 0
  4.     READ = auto()    # 1
  5.     WRITE = auto()   # 2
  6.     DELETE = auto()  # 4
  7.     ADMIN = READ | WRITE | DELETE  # 7
  8. user_perms = Permission.READ | Permission.WRITE
  9. print(user_perms)                 # Permission.READ|WRITE
  10. print(Permission.READ in user_perms)   # True
  11. print(Permission.DELETE in user_perms) # False
  12. user_perms |= Permission.DELETE
  13. print(Permission.ADMIN in user_perms)  # True
复制代码

权限检查可以封装为函数,用“所需权限是否包含在用户权限中”判断:
  1. def can_access(required, user_permission):
  2.     return required in user_permission
  3. print(can_access(Permission.READ, Permission.READ | Permission.WRITE))
  4. print(can_access(Permission.DELETE, Permission.READ | Permission.WRITE))
复制代码

五、实战:HTTP状态码枚举
IntEnum很适合包装HTTP状态码。下面枚举包含200、201、400、401、403、404、500,并提供了判断成功和客户端错误的方法:
  1. from enum import IntEnum
  2. class HttpStatus(IntEnum):
  3.     OK = 200
  4.     CREATED = 201
  5.     BAD_REQUEST = 400
  6.     UNAUTHORIZED = 401
  7.     FORBIDDEN = 403
  8.     NOT_FOUND = 404
  9.     INTERNAL_ERROR = 500
  10.     def is_success(self):
  11.         return 200 <= self.value < 300
  12.     def is_client_error(self):
  13.         return 400 <= self.value < 500
  14. def handle_response(status_code, body):
  15.     status = HttpStatus(status_code)
  16.     if status.is_success():
  17.         print(f'✓ {status.name}: {body}')
  18.     elif status.is_client_error():
  19.         print(f'✗ {status.name}: 客户端错误')
  20.     else:
  21.         print(f'⚠ {status.name}: 服务器错误')
  22. handle_response(200, '操作成功')
  23. handle_response(404, None)
复制代码

六、实战:配置日志级别
普通Enum默认不支持>=比较,但可以通过实现__ge__来支持。原文示例将DEBUG、INFO、WARNING、ERROR分别设为10、20、30、40:
  1. from enum import Enum
  2. class LogLevel(Enum):
  3.     DEBUG = 10
  4.     INFO = 20
  5.     WARNING = 30
  6.     ERROR = 40
  7.     def __ge__(self, other):
  8.         if self.__class__ is other.__class__:
  9.             return self.value >= other.value
  10.         return NotImplemented
  11. class LogConfig:
  12.     current_level = LogLevel.INFO
  13.     @classmethod
  14.     def set_level(cls, level):
  15.         if not isinstance(level, LogLevel):
  16.             raise TypeError(f'请使用LogLevel枚举,而不是 {type(level)}')
  17.         cls.current_level = level
  18.     @classmethod
  19.     def should_log(cls, level):
  20.         return level >= cls.current_level
  21. LogConfig.set_level(LogLevel.DEBUG)
  22. print(LogConfig.should_log(LogLevel.INFO))  # True
复制代码

七、选型与使用建议
通用枚举用Enum,重点是类型安全和名字清晰,不应与整数混用;需要兼容整数比较或运算时用IntEnum;需要位运算组合时用Flag;不关心具体值、只需自动编号时用auto()。成员命名通常采用全大写,类名采用PascalCase。代码中一旦出现0、1、2这类状态码,优先考虑用枚举表达。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-25 13:56 , Processed in 0.022128 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部