enum
模块可以定义具有迭代和比较能力的枚举类型。它可以为各个值创建具有明确意义的符号标记,而不是使用整数或者字符串。
一个新的枚举类型使用类语法,通过继承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))
成员产生的顺序与它们在类中声明的顺序一致。name
和value
的值并不会影响这个顺序。
$ python3 enum_iterate.py
new = 7
incomplete = 6
invalid = 5
wont_fix = 4
in_progress = 3
fix_committed = 2
fix_released = 1
因为枚举类的成员并没有顺序,所以他们只支持对象身份比较和相等比较。
# enum_comparison.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_comparison.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_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
是另外一些成员的别名,所以它们在我们迭代这个枚举类的时候并不会出现。成员的名字被认为是第一个关联到这个值的名字。
$ 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_released: True
如果我们需要枚举成员具有唯一的值,可以给枚举类添加
@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
当枚举类被解释执行的时候,如果成员具有重复的值,将会引发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 'BugStatus' >:
by_design -> wont_fix, closed -> fix_released
在一些情况下,通过程序化创建枚举类比将它们硬编码到一个类中更加方便。在这些场景中,枚举类允许将成员名字和对应的值传递给类的构造方法。
# enum_programmatic_create.py
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('\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))
在这个例子中,我们给names
参数传递了一个包含两个元素的元组的列表,而不是一个只包含成员名字的字符串。这样我们就可以通过这种方式创建出和enum_create.py
中顺序一样的枚举类。
$ 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号和一个当前状态可以转换到的状态列表。
$ 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
原文点这里
参考:
1.enum模块的官方文档
2.PEP 435 - 向Python标准库中添加枚举类型。
3.flufl.enum -
enum
最初的灵感来源 by Barry Warsaw