Python Enum:如何在Python中构建枚举

 

学习如何使用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枚举。
 
 

更多