Python Module之enum-枚举类型

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))

成员产生的顺序与它们在类中声明的顺序一致。namevalue的值并不会影响这个顺序。

$ 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_designclosed是另外一些成员的别名,所以它们在我们迭代这个枚举类的时候并不会出现。成员的名字被认为是第一个关联到这个值的名字。

$ 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 <enum '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

猜你喜欢

转载自blog.csdn.net/gggavin/article/details/78961508