Python的enum模块通过创建枚举类将相关常量组织为类型安全的成员,每个成员具有唯一身份、可迭代且支持名称与值访问;相比传统魔术字符串或数字常量,enum提供强类型检查、防止拼写错误、提升可读性与维护性;结合auto()可自动生成值,Flag类支持位运算组合状态;序列化时需转换为值或名称以兼容JSON,反序列化则通过构造函数或下标恢复枚举成员,数据库存储常映射为字符串或整数字段,整体显著增强代码健壮性与清晰度。

Python的
enum
理解Python的
enum
你可以这样定义一个枚举:
from enum import Enum, auto
class TrafficLight(Enum):
RED = 1
YELLOW = 2
GREEN = 3
class Status(Enum):
PENDING = auto()
APPROVED = auto()
REJECTED = auto()
# 访问枚举成员
print(TrafficLight.RED) # 输出: <TrafficLight.RED: 1>
print(TrafficLight.RED.name) # 输出: RED
print(TrafficLight.RED.value) # 输出: 1
# 迭代枚举
for light in TrafficLight:
print(f"{light.name} is {light.value}")
# 比较
if TrafficLight.RED == TrafficLight.RED:
print("Same light!")
# 从值获取枚举成员
print(TrafficLight(1)) # 输出: <TrafficLight.RED: 1>
# auto() 的用法,让Python自动为你分配值
# 默认从1开始递增,也可以自定义行为
print(Status.PENDING.value) # 输出: 1
print(Status.APPROVED.value) # 输出: 2我个人觉得,当你发现代码里开始出现一堆魔术字符串或者数字,并且这些值其实是代表某种状态或类型时,
enum
"PENDING"
"APPROVED"
enum
立即学习“Python免费学习笔记(深入)”;
我们都知道,Python里没有像C++或Java那样的
const
MAX_RETRIES = 5
MAX_RETRIES
# 传统方式
ORDER_STATUS_PENDING = "pending"
ORDER_STATUS_APPROVED = "approved"
ORDER_STATUS_REJECTED = "rejected"
def process_order(status):
if status == ORDER_STATUS_PENDING:
# ...
elif status == "approved": # 糟糕,这里直接用了字符串而不是常量
# ...这里的问题很明显:
ORDER_STATUS_PENDING
而
enum
TrafficLight.RED
TrafficLight
TrafficLight
TrafficLight.RED
1
"red"
TrafficLight(value)
TrafficLight[name]
TrafficLight.RED is TrafficLight.RED
True
我记得有一次,在处理一个订单状态的系统时,因为早期没有使用枚举,导致各种地方对订单状态的字符串拼写不一致,最后排查问题简直是噩梦。引入枚举后,所有状态都集中管理,类型错误也大大减少,代码清晰度提升不止一个档次。所以,只要是表示一组固定、有限且有意义的值,我都强烈建议使用
enum
在实际项目中,
enum
迭代与成员获取: 前面提到了迭代,但你可能需要一个成员列表或者一个值到成员的映射。
# 获取所有成员的列表 all_lights = list(TrafficLight) # [<TrafficLight.RED: 1>, <TrafficLight.YELLOW: 2>, <TrafficLight.GREEN: 3>] # 获取所有值的列表 all_values = [light.value for light in TrafficLight] # [1, 2, 3] # 获取所有名称的列表 all_names = [light.name for light in TrafficLight] # ['RED', 'YELLOW', 'GREEN']
auto()
auto()
_generate_next_value_
class MyEnum(Enum):
def _generate_next_value_(name, start, count, last_values):
# 自定义生成逻辑,例如从100开始,每次加10
return 100 + count * 10
FIRST = auto()
SECOND = auto()
THIRD = auto()
print(MyEnum.FIRST.value) # 100
print(MyEnum.SECOND.value) # 110
print(MyEnum.THIRD.value) # 120这在定义一些内部使用的状态码时特别有用,你不用去手动编号,也不用担心编号冲突。
Flag
enum.Flag
|
&
~
from enum import Flag, auto
class Permissions(Flag):
NONE = 0
READ = auto() # 1
WRITE = auto() # 2
EXECUTE = auto() # 4
ALL = READ | WRITE | EXECUTE # 7
user_perms = Permissions.READ | Permissions.WRITE
print(user_perms) # <Permissions.READ|WRITE: 3>
if Permissions.READ in user_perms: # 也可以用 `in` 操作符
print("User can read.")
if user_perms & Permissions.EXECUTE: # 或者用 `&`
print("User can execute.") # 不会打印
# 检查是否包含所有权限
if user_perms == Permissions.ALL:
print("User has all permissions.") # 不会打印Flag
避免与原始值直接比较的陷阱: 虽然
TrafficLight.RED.value
1
TrafficLight.RED == 1
False
__eq__
.value
TrafficLight.RED.value == 1
在将数据存储到数据库、写入文件或通过网络传输时,序列化和反序列化是必不可少的环节。Python的
enum
JSON 序列化: 默认情况下,当你尝试直接用
json.dumps()
TypeError
import json
from enum import Enum
class Color(Enum):
RED = 'red'
BLUE = 'blue'
data = {"favorite_color": Color.RED, "other_data": "some string"}
# 这样会报错: TypeError: Object of type Color is not JSON serializable
# json_output = json.dumps(data)
# 最佳实践:在序列化时转换成其值或名称
def enum_serializer(obj):
if isinstance(obj, Enum):
return obj.value # 或者 obj.name,取决于你的需求
raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")
json_output_value = json.dumps(data, default=enum_serializer)
print(json_output_value) # {"favorite_color": "red", "other_data": "some string"}
# 如果选择序列化为名称
def enum_name_serializer(obj):
if isinstance(obj, Enum):
return obj.name
raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")
json_output_name = json.dumps(data, default=enum_name_serializer)
print(json_output_name) # {"favorite_color": "RED", "other_data": "some string"}通常我会选择序列化为
.value
.name
.name
JSON 反序列化: 反序列化时,你需要将JSON中的字符串或数字转换回枚举成员。这通常需要手动查找。
# 从值反序列化
json_str_value = '{"favorite_color": "red", "other_data": "some string"}'
loaded_data_value = json.loads(json_str_value)
# 假设我们知道 'favorite_color' 对应 Color 枚举
loaded_data_value["favorite_color"] = Color(loaded_data_value["favorite_color"])
print(loaded_data_value["favorite_color"]) # <Color.RED: 'red'>
# 从名称反序列化
json_str_name = '{"favorite_color": "RED", "other_data": "some string"}'
loaded_data_name = json.loads(json_str_name)
loaded_data_name["favorite_color"] = Color[loaded_data_name["favorite_color"]]
print(loaded_data_name["favorite_color"]) # <Color.RED: 'red'>这里要注意的是,
Color(value)
Color[name]
ValueError
KeyError
数据库存储: 在数据库中存储枚举,通常有两种做法:
value
value
name
name
name
name
例如,在使用SQLAlchemy这样的ORM时,你可以定义一个自定义类型来处理枚举的映射:
from sqlalchemy import TypeDecorator, String, Integer
from sqlalchemy.dialects import postgresql # 举例,也可以是其他方言
class EnumAsText(TypeDecorator):
impl = String # 存储为字符串
def __init__(self, enum_class):
TypeDecorator.__init__(self)
self.enum_class = enum_class
def process_bind_param(self, value, dialect):
if value is None:
return None
return value.name # 存储枚举的名称
def process_result_value(self, value, dialect):
if value is None:
return None
return self.enum_class[value] # 从名称反序列化为枚举成员
# 在模型中使用
# class MyModel(Base):
# __tablename__ = 'my_table'
# id = Column(Integer, primary_key=True)
# status = Column(EnumAsText(Status)) # 假设Status是你的枚举这种方式的好处是,你在Python代码中始终使用类型安全的枚举成员,而数据库中存储的是可读性强的字符串,方便调试和直接查询。
总的来说,处理枚举的序列化与反序列化,核心就是要在序列化时将其转换为基础类型(字符串或数字),在反序列化时再将其转换回枚举成员。这虽然需要一些额外的代码,但换来的是代码的健壮性和可维护性,这笔买卖怎么看都划算。
以上就是如何理解Python的enum模块(枚举)?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号