enum - 枚举类型
enum
模块定义了支持迭代和比较功能的枚举类型。它可用于为一些含有意义值,创建良好定义的符号,从而可以避免使用文字型整数(literal integers)或字符串。
创建枚举
创建一个新的枚举,可以通过使用class
语法创建Enum
的子类,并添加描述值的类属性来实现。
# enum_create.py
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('\nMember name: {}'.format(BugStatus.wont_fix.name))
print('Member value: {}'.format(BugStatus.wont_fix.value))
在解析此Enum
类的时候,其成员会被传换成实例。每个实例拥有一个name
属性,对应其成员名称,以及一个value
属性,对应在此枚举类中,名称被赋予的值。
$ python3 enum_create.py
Member name: wont_fix
Member value: 4
迭代
对一个枚举类进行迭代,可以得到这个枚举的所有成员。
# enum_iterate.py
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))
迭代产生的成员按照迭代类中生命的顺序生成。迭代时,成员的名称或值不会被用于排序。
$ python3 enum_iterate.py
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
枚举比较
因为枚举的成员是无序的,所以枚举只支持同一(identity)和相等(equality)两种比较。
# enum_comparision.py
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('\n'.join(' ' + s.name for s in sorted(BugStatus)))
except TypeError as err:
print(' Cannot sort: {}'.format(err))
若对其使用大于、小于比较运算符,会导致TypeError
异常。
$ python3 enum_comparision.py
Equality: False True
Identity: False True
Ordered by value:
Cannot sort: unorderable types: BugStatus() < BugStatus()
如果希望枚举类型表现的更像是一个数字,例如,希望它们支持比较操作,可以使用IntEnum
类。
# enum_intenum.py
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('\n'.join(' ' + s.name for s in sorted(BugStatus)))
$ python3 enum_intenum.py
Ordered by value:
fix_released
fix_committed
in_progress
wont_fix
invalid
incomplete
new
枚举值的唯一性
拥有相同值的两个枚举成员,会被当作对同一个枚举成员对象的别名引用。在对Enum
进行迭代时,不会因为别名导致重复的值出现多次。
# enum_aliases.py
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('\nSame: 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
进行迭代的时候,它们不会再单独出现在输出中。对于一个枚举成员来说,它的权威名称是第一次给它赋值时使用的名称。
$ python3 enum_aliases.py
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
Same: by_design is wont_fix: True
Same: closed is fix_committed: True
若想要却表每个成员都有一个唯一的值,可以对Enum
加上一个@unique
装饰器。
# enum_unique_enforce.py
import 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
异常。
$ python3 enum_unique_enforce.py
Traceback (most recent call last):
File "enum_unique_enforce.py", line 11, in <module>
class BugStatus(enum.Enum):
File ".../lib/python3.5/enum.py", line 573, in unique
(enumeration, alias_details))
ValueError: duplicate values found in <enum 'BugStatus'>:
by_design -> wont_fix, closed -> fix_released`
在运行时创建枚举
在一些情况下,相比将枚举成员硬编码在枚举类中,在运行时用程序创建枚举是更方便的做法。对于这种情况,Enum
也支持向其构造器传入成员名称和值的方式创建枚举。
# enum_programmatic_create.py
import enum
BugStatus = enum.Enum(
value='BugStatus',
names=('fix_released fix_released in_progress '
'wont_fix invalid incomplete new'),
)
print('Member: {}'.format(BugStatus.new))
print('\nAll members:')
for status in BugStatus:
print('{:15} = {}'.format(status.name, status.value))
value
参数是枚举的名称,用于构建成员的表示形式。names
参数列出了枚举的成员。当传入单个字符串时,字符串会以空格和逗号为分隔符分割,并将分割后生成的符号标记用作成员名称,同时这些成员将自动被赋值,赋值从1开始。
$ python3 enum_programmatic_create.py
Member: BugStatus.new
All members:
fix_released = 1
fix_committed = 2
in_progress = 3
wont_fix = 4
invalid = 5
incomplete = 6
new = 7
若想要更好地控制成员关联的值,之前的names
字符串参数,可以使用能够映射名称、值关系的二元元组序列或字典来代替。
# enum_programmatic_mapping.py
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
枚举。
$ python3 enum_programmatic_mapping.py
All members:
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
非整数成员值
枚举成员值不仅仅限于整数。实际上,任何类型的对象都可以与一个枚举成员相关联,如果该值是一个元组,成员将会作为单个参数传给__init__()
。
# enum_tuple_values.py
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(例如,可能要存在数据库中)和当前状态的有效转移形式(transition)的列表。
$ python3 enum_tuple_values.py
Name: BugStatus.in_progress
Value: (3, ['new', 'fix_committed'])
Custom attribute: ['new', 'fix_committed']
Using attribute: True
对于更复杂的情况,使用元组可能不太方便。由于成员值可以是任何类型的对象,对于每个枚举值需要追踪多个不同的属性的情况,可以使用字典。多个值会被作为唯一的参数(除了self
以外)直接传入__init__()
。
# enum_complex_values.py
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))
这个例子和再之前的例子表达相同的数据,但这里使用了字典,而非元组。
$ python3 enum_complex_values.py
Name: BugStatus.in_progress
Value: {'transitions': ['new', 'fix_committed'], 'num': 3}
Custom attribute: ['new', 'fix_committed']
Using attribute: True