跳转至

6.12.enum

from toolkit import H
import enum
import json
Windows 10
Python 3.7.3 @ MSC v.1915 64 bit (AMD64)
Latest build date 2020.11.13

enum 模块提供的类和函数如下:

d = H(enum).dicts
print("class", d["class"])
print("function", d["function"])
module
class ['Enum', 'EnumMeta', 'Flag', 'IntEnum', 'IntFlag', 'auto']
function ['unique']

创建枚举类

Enum 的成员在类被解析的时候转化为实例。每一个实例都有一个 name属性对应成员的名称,一个 value 属性对应在类中赋值给成员名称的值。

class BugStatus(enum.Enum):
    new = 7
    incomplete = 6
    invalid = 5
    wont_fix = 4
    in_progress = 3
    fix_committed = 2
    fix_released = 1

print(type(BugStatus))
print('Member name: {}'.format(BugStatus.wont_fix.name))
print('Member value: {}'.format(BugStatus.wont_fix.value))
<class 'enum.EnumMeta'>
Member name: wont_fix
Member value: 4

不能通过直接引用Enum 的成员的方式和其值作比较:

print(BugStatus.new == 7)
print(BugStatus.new.value == 7)
False
True

迭代

成员以它们在类中被定义的顺序逐个产生, 成员的名称和值没有以任何方式用来对它们进行排序。

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))
new             = 7
incomplete      = 6
invalid         = 5
wont_fix        = 4
in_progress     = 3
fix_committed   = 2
fix_released    = 1

Enum 类成员比较

由于枚举成员不是有序的,所以它们只支持按 id 或按值的相等性进行比较。对枚举成员应用大于和小于比较操作符将抛出 TypeError 异常。

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))
Equality: False True
Identity: False True
Ordered by value:
  Cannot sort: '<' not supported between instances of 'BugStatus' and
'BugStatus'

若想使枚举成员表现得更类似于数字 —— 举例来说,要支持大于或小于比较,则需要使用 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('\n'.join('  ' + s.name for s in sorted(BugStatus)))
Ordered by value:
  fix_released
  fix_committed
  in_progress
  wont_fix
  invalid
  incomplete
  new

唯一的枚举值

具有相同值的 Enum 成员会被当作同一对象的别名引用进行跟踪。别名不会导致 Enum 的迭代器里面出现重复值。

Python 内部会对小整数 [-5,257) 进行缓存,所有对小整数的声明都指向同一个对象。但是在 Enum 里面大于 257 的重复整数也会被指向同一个对象。

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

因为 by_designclosed 是其他成员的别名, 在遍历 Enum 的时候,它们都不会出现在输出中。枚举成员中第一个关联到成员值的名称是规范名称。

如果想要枚举成员只包含唯一值, 可以在 Enum 上加上装饰器 @unique。当 Enum 类被解释器执行的时候,重复的成员会触发一个 ValueError 异常。

import enum

try:
    @enum.unique
    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
except ValueError as e:
    print(e)
duplicate values found in <enum 'BugStatus'>: by_design -> wont_fix,
closed -> fix_released

动态创建枚举类

在某种情况下,比起以类定义的方式硬编码枚举,用在编码中动态创建的枚举的方式更加方便。针对这种情况, 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('\nAll members:')
for status in BugStatus:
    print('{:15} = {}'.format(status.name, status.value))
Member: BugStatus.new

All members:
fix_released    = 1
fix_committed   = 2
in_progress     = 3
wont_fix        = 4
invalid         = 5
incomplete      = 6
new             = 7

为了更加方便的控制枚举成员的值,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))
All members:
new             = 7
incomplete      = 6
invalid         = 5
wont_fix        = 4
in_progress     = 3
fix_committed   = 2
fix_released    = 1

非整数成员值

枚举成员值并没有限制为整数类型。实际上,枚举成员可以关联任何对象的类型。如果值是一个元组,==成员值会作为独立的参数传递给 __init__() 函数。==因此,重写 __init__() 函数或 __new__() 函数可以为成员值添加新属性。

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))
Name: BugStatus.in_progress
Value: (3, ['new', 'fix_committed'])
Custom attribute: ['new', 'fix_committed']
Using attribute: True

在这个例子中, 每一个成员值都是包含数字 ID(方便于存储在数据库中)和transitions列表。

对于更复杂的情况来说,元组可能会变得比较笨拙。由于成员值可以是任何类型的对象,字典可以被用于需要跟踪每个枚举值的独立属性的情况。复杂的值作为除了 self 之外唯一的参数传递给 __init__()

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))
Name: BugStatus.in_progress
Value: {'num': 3, 'transitions': ['new', 'fix_committed']}
Custom attribute: ['new', 'fix_committed']
Using attribute: True

重写 __new__() 函数可添加属性。

class AutoNumber(enum.Enum):
    def __new__(cls):
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)
        obj.test = value
        return obj

class Color(AutoNumber):
    RED = ()
    GREEN = ()
    BLUE = ()

print(Color.RED.test)
1

Note

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor massa, nec semper lorem quam in massa.

$$ \sum $$

$$ \sum $$

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor massa, nec semper lorem quam in massa.

a = 1

print("test")
test