1. enum枚举
枚举是一组符号名称(枚举成员)的集合,枚举成员应该是唯一的、不可变的。在枚举中,可以对成员进行恒等比较,并且枚举本身是可迭代的。
1.1 创建枚举
可以使用class语法派生Enum并增加描述值的类属性来定义一个新枚举。
import enum class BugStatus(enum.Enum): new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 print(' Member name: {}'.format(BugStatus.wont_fix.name)) print('Member value: {}'.format(BugStatus.wont_fix.value))
解析这个类时,Enum的成员会被转换为实例。每个实例有一个对应成员名的name属性,另外有一个value属性,对应为类定义中的名所赋的值。
1.2 迭代
迭代处理enum类会产生枚举的各个成员。
import enum class BugStatus(enum.Enum): new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 for status in BugStatus: print('{:15} = {}'.format(status.name, status.value))
这些成员按它们在类定义中声明的顺序生成。不会用名和值来对它们排序。
1.3 比较Enum
由于枚举成员是无序的,所以它们只支持按同一性和相等性进行比较。
import enum class BugStatus(enum.Enum): new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 actual_state = BugStatus.wont_fix desired_state = BugStatus.fix_released print('Equality:', actual_state == desired_state, actual_state == BugStatus.wont_fix) print('Identity:', actual_state is desired_state, actual_state is BugStatus.wont_fix) print('Ordered by value:') try: print(' '.join(' ' + s.name for s in sorted(BugStatus))) except TypeError as err: print(' Cannot sort: {}'.format(err))
大于或小于比较符会产生TypeError异常。
有些枚举中的成员要表现得更像数字,例如,要支持比价,对于这些枚举要使用IntEnum类。
import enum class BugStatus(enum.IntEnum): new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 print('Ordered by value:') print(' '.join(' ' + s.name for s in sorted(BugStatus)))
1.4 唯一枚举值
有相同值的Enum成员会被处理为同一个成员对象的别名引用。别名可以避免Enum的迭代器中出现重复的值。
import enum class BugStatus(enum.Enum): new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 by_design = 4 closed = 1 for status in BugStatus: print('{:15} = {}'.format(status.name, status.value)) print(' Same: by_design is wont_fix: ', BugStatus.by_design is BugStatus.wont_fix) print('Same: closed is fix_released: ', BugStatus.closed is BugStatus.fix_released)
由于by_design和closed是其他成员的别名,迭代处理Enum时它们不会单独出现在输出中。一个成员的规范名是与这个值关联的第一个名字。
如果要求所有成员有唯一的值,则要为Enum增加@unique修饰符。
class BugStatus(enum.Enum): new = 7 incomplete = 6 invalid = 5 wont_fix = 4 in_progress = 3 fix_committed = 2 fix_released = 1 # This will trigger an error with unique applied. by_design = 4 closed = 1
解释Enum类时,有重复值的成员会触发一个ValueError异常。
1.5 通过编程创建枚举
有些情况下,通过编程创建枚举会更方便,而不是在类定义中硬编码定义枚举。在这些情况下,Enum还支持向类构造函数传递成员名和值。
import enum BugStatus = enum.Enum( value='BugStatus', names=('fix_released fix_committed in_progress ' 'wont_fix invalid incomplete new'), ) print('Member: {}'.format(BugStatus.new)) print(' All members:') for status in BugStatus: print('{:15} = {}'.format(status.name, status.value))
value参数是枚举名,用于构建成员的表示。names参数会列出枚举的成员。传递单个字符串时,会按空白符和逗号拆分,所得到的token会被用作成员名,这些成员还会自动赋值,从1开始。
要想更多地控制与成员关联的值,可以把names字符串替换为一个由两部分元组构成的序列或者一个将名映射到值的字典。
import enum BugStatus = enum.Enum( value='BugStatus', names=[ ('new', 7), ('incomplete', 6), ('invalid', 5), ('wont_fix', 4), ('in_progress', 3), ('fix_committed', 2), ('fix_released', 1), ], ) print('All members:') for status in BugStatus: print('{:15} = {}'.format(status.name, status.value))
在这个例子中,指定了一个两部分元组的列表而不是一个只包含成员名的字符串。这样就可以按enum_create.py中定义的同样的顺序利用成员重新构造BugStatus枚举。
1.6 非整数成员值
Enum成员值并不仅限于整数。任何类型的对象都可以与成员关联。如果值是一个元组,那么成员会作为单个参数被传递到__init__()。
import enum class BugStatus(enum.Enum): new = (7, ['incomplete', 'invalid', 'wont_fix', 'in_progress']) incomplete = (6, ['new', 'wont_fix']) invalid = (5, ['new']) wont_fix = (4, ['new']) in_progress = (3, ['new', 'fix_committed']) fix_committed = (2, ['in_progress', 'fix_released']) fix_released = (1, ['new']) def __init__(self, num, transitions): self.num = num self.transitions = transitions def can_transition(self, new_state): return new_state.name in self.transitions print('Name:', BugStatus.in_progress) print('Value:', BugStatus.in_progress.value) print('Custom attribute:', BugStatus.in_progress.transitions) print('Using attribute:', BugStatus.in_progress.can_transition(BugStatus.new))
在这个例子中,每个成员值分别是一个元组,包括数值ID(如可能存储在一个数据库中的ID)和从当前状态迁移的一个合法变迁列表。
对于更复杂的情况,元组可能变得很难用。因为成员值可以是任意类型的对象,如果对每个enum值都需要跟踪大量单独的属性,那么这些情况下可以使用字典。复杂的值可以直接作为唯一参数传递到__init__()而不是self。
import enum class BugStatus(enum.Enum): new = { 'num': 7, 'transitions': [ 'incomplete', 'invalid', 'wont_fix', 'in_progress', ], } incomplete = { 'num': 6, 'transitions': ['new', 'wont_fix'], } invalid = { 'num': 5, 'transitions': ['new'], } wont_fix = { 'num': 4, 'transitions': ['new'], } in_progress = { 'num': 3, 'transitions': ['new', 'fix_committed'], } fix_committed = { 'num': 2, 'transitions': ['in_progress', 'fix_released'], } fix_released = { 'num': 1, 'transitions': ['new'], } def __init__(self, vals): self.num = vals['num'] self.transitions = vals['transitions'] def can_transition(self, new_state): return new_state.name in self.transitions print('Name:', BugStatus.in_progress) print('Value:', BugStatus.in_progress.value) print('Custom attribute:', BugStatus.in_progress.transitions) print('Using attribute:', BugStatus.in_progress.can_transition(BugStatus.new))
这个例子描述了与前例相同的数据,不过使用的是字典而非元组。