学习如何使用Python内置的enum模块创建和使用枚举。
如果你曾用过C++或Java编程语言,你可能使用过枚举来创建命名常量。当你的变量只能取固定数量的值时,枚举非常有用——通常是相关的,比如一周的天数、学生的成绩、订单状态等等。
然而,Python并没有显式的枚举数据类型。但你可以使用Python标准库中的enum模块来创建枚举。本教程将教你如何做到这一点。
让我们开始吧!
什么是枚举,以及为什么要使用它们?
枚举代表“列举”,由一组预定义的命名常量组成。这些常量通常是相关的。常见的例子包括一年中的月份、一周的天数、成绩、订单和任务状态。
总而言之,枚举实际上是一个相关常量的集合,每个常量都有一个有意义的名称与之关联。
在Python中,你可以使用enum模块创建枚举(我们稍后会这样做!)。
为什么使用枚举
使用枚举有助于提高代码的清晰性和可维护性。以下是原因:
- 枚举通过用有意义的标签替换魔法数或字符串来增强代码的清晰性。它们还使代码更加自我描述化,因为枚举成员的名称传达了它们的用途。
- 枚举通过提供简单的方法来定义和管理相关常量,提高了代码的可维护性。
- 通过将变量赋值限制为仅有效的枚举值,枚举还确保了类型安全。
- 枚举便于对相关常量进行迭代和比较。
现在让我们在Python中创建我们的第一个枚举。
使用Python的Enum类创建枚举
我们将创建一个TaskStatus
枚举。
首先,我们从enum模块中导入Enum
类以创建枚举。
然后我们定义一个新的类TaskStatus
,它继承自Enum
以创建一个枚举。每个成员都用一个唯一的名称和一个可选的值来定义,如下所示:
from enum import Enum
class TaskStatus(Enum):
TODO = 0
IN_PROGRESS = 1
DONE = 2
ABANDONED = -1
我们创建的所有枚举成员都是Enum类的实例。你可以通过调用isinstance()
函数来验证这一点,如下所示:
print(isinstance(TaskStatus.TODO,Enum))
Output >>> True
让我们通过将TaskStatus
枚举转换为列表来打印出所有成员:
print(list(TaskStatus))
你应该会看到以下输出:
Output >>>
[<TaskStatus.TODO: 0>, <TaskStatus.IN_PROGRESS: 1>, <TaskStatus.DONE: 2>, <TaskStatus.ABANDONED: -1>]
所有枚举成员都有一个名称和一个值。这意味着你可以通过名称访问枚举成员,例如TaskStatus.TODO
。或者你可以通过值访问它们,例如TaskStatus(0)
。
使用Python枚举
现在我们已经创建了一个简单的TaskStatus
枚举,让我们学习如何执行一些简单的任务,比如迭代枚举成员。
迭代枚举
在Python中,你可以像处理任何可迭代对象一样处理枚举。例如,你可以使用len()
函数来计算枚举成员的数量:
num_statuses = len(TaskStatus)
print(num_statuses)
Output >>> 4
你也可以像迭代Python的可迭代对象(如列表)一样迭代枚举。在下面的for循环中,我们访问每个枚举成员的名称和值并将它们打印出来:
for status in TaskStatus:
print(status.name, status.value)
输出如下:
Output >>>
TODO 0
IN_PROGRESS 1
DONE 2
ABANDONED -1
枚举中的排序
在这个例子中,状态及其对应的数值如下:
- TODO: 0
- IN_PROGRESS: 1
- DONE: 2
- ABANDONED: -1
但是你也可以使用auto()
辅助函数进行默认排序。这样做时,如果枚举中有‘n’个成员,则分配的值是1到n。但你可以传入一个起始值,比如k,auto(k)
,枚举将从k开始并到k+n。
让我们修改TaskStatus
枚举,如下所示:
from enum import Enum, auto
class TaskStatus(Enum):
TODO = auto()
IN_PROGRESS = auto()
DONE = auto()
ABANDONED = auto()
现在让我们打印出成员:
print(list(TaskStatus))
我们可以看到这些值如预期的是1到4:
Output >>>
[<TaskStatus.TODO: 1>, <TaskStatus.IN_PROGRESS: 2>, <TaskStatus.DONE: 3>, <TaskStatus.ABANDONED: 4>]
基于TaskStatus枚举的扩展示例
现在让我们基于已有的TaskStatus
枚举进行扩展。在你的工作目录中创建一个task.py文件,其中包含以下版本的枚举:
# task.py
from enum import Enum
class TaskState(Enum):
TODO = 0
IN_PROGRESS = 1
DONE = 2
ABANDONED = -1
假设我们有一个任务,其名称和当前状态。有效状态之间的转换如下所示:
让我们创建一个Task
类:
class Task:
def __init__(self, name, state):
self.name = name
self.state = state
def update_state(self, new_state):
# 根据当前状态定义有效的状态转换
valid_transitions = {
TaskState.TODO: [TaskState.IN_PROGRESS, TaskState.ABANDONED],
TaskState.IN_PROGRESS: [TaskState.DONE, TaskState.ABANDONED],
TaskState.DONE: [],
TaskState.ABANDONED: []
}
# 检查新状态是否是从当前状态的有效转换
if new_state in valid_transitions[self.state]:
self.state = new_state
else:
raise ValueError(f"Invalid state transition from {self.state.name} to {new_state.name}")
我们有一个update_status()
方法,该方法检查给定当前状态的转换到新状态是否有效。对于无效的转换,将引发ValueError异常。
这是Task
类的一个实例:“写报告”任务,状态为TODO:
# 创建一个初始状态为“待办”的新任务
task = Task("Write Report", TaskState.TODO)
# 打印任务详情
print(f"Task Name: {task.name}")
print(f"Current State: {task.state.name}")
Output >>>
Task Name: Write Report
Current State: TODO
将任务状态更新为IN_PROGRESS应该是有效的状态转换:
# 将任务状态更新为“进行中”
task.update_state(TaskState.IN_PROGRESS)
print(f"Updated State: {task.state.name}")
Output >>> Updated State: IN_PROGRESS
一旦任务完成,我们可以将其状态更新为DONE:
# 将任务状态更新为“完成”
task.update_state(TaskState.DONE)
print(f"Updated State: {task.state.name}")
Output >>> Updated State: DONE
但是如果你尝试将状态更新为无效的状态,比如尝试将DONE更新为TODO,你将遇到ValueError异常:
# 尝试将任务状态更新到无效状态
task.update_state(TaskState.TODO)
以下是从DONE到TODO无效状态转换引发的ValueError的回溯:
Traceback (most recent call last):
File "/home/balapriya/enums/task.py", line 46, in
task.update_state(TaskState.TODO)
File "/home/balapriya/enums/task.py", line 30, in update_state
raise ValueError(f"Invalid state transition from {self.state.name} to {new_state.name}")
ValueError: Invalid state transition from DONE to TODO
总结
在本教程中,我们通过编写一个简单的TaskStatus枚举学习了如何在Python中构建枚举。我们学习了如何访问枚举成员并迭代它们。
此外,我们还学习了如果选择使用auto()
辅助函数为枚举成员设置值时,默认排序是如何工作的。然后,我们尝试在一个更有帮助的示例中使用TaskStatus枚举。