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_designclosed是其他成员的别名,在对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

See Also

results matching ""

    No results matching ""