You are here:  Home » Python » enum模块支持枚举的详解(42)Python语言(必读进阶学习教程)(参考资料)

枚举是一组绑定到唯一常量值的符号名称(成员)。 在枚举中,成员可以按身份进行比较,并且枚举本身可以迭代。

模块内容

该模块定义了四个可用于定义唯一的名称和值集的枚举类:Enum、IntEnum、Flag 和 IntFlag。 它还定义了一个装饰器 unique() 和一个助手 auto。

class enum.Enum
用于创建枚举常量的基类。 有关替代构造语法,请参阅功能 API 部分。
class enum.IntEnum
用于创建也是 int 子类的枚举常量的基类。
class enum.IntFlag
用于创建枚举常量的基类,这些常量可以使用按位运算符组合而不会丢失其 IntFlag 成员资格。 IntFlag 成员也是 int 的子类。
class enum.Flag
用于创建枚举常量的基类,这些常量可以使用按位运算组合而不会丢失其 Flag 成员资格。
enum.unique()
确保只有一个名称绑定到任何一个值的枚举类装饰器。
class enum.auto
实例被替换为枚举成员的适当值。 初始值从 1 开始。

新的3.6版:FlagIntFlagauto

创建枚举

枚举是使用类语法创建的,这使得它们易于读写。 Functional API 中描述了另一种创建方法。 要定义一个枚举,子类 Enum 如下:

>>>
>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...

注意

枚举成员值
成员值可以是任何东西:int、str 等。如果确切的值不重要,您可以使用自动实例,系统会为您选择一个合适的值。 如果将 auto 与其他值混合使用,则必须小心。

注意

命名法

  • Color 类是枚举(或枚举)
  • Color.RED、Color.GREEN 等属性是枚举成员(或枚举成员),并且是功能常量。
  • 枚举成员有名称和值(Color.RED 的名称是 RED,Color.BLUE 的值是 3,等等)

注意

即使我们使用类语法来创建枚举,枚举也不是普通的 Python 类。 请参阅枚举有何不同? 更多细节。

枚举成员具有人类可读的字符串表示形式:

>>>
>>> print(Color.RED)
Color.RED

…而他们的代表有更多信息:

>>>
>>> print(repr(Color.RED))
<Color.RED: 1>

枚举成员的类型是它所属的枚举:

>>>
>>> type(Color.RED)
<enum 'Color'>
>>> isinstance(Color.GREEN, Color)
True
>>>

枚举成员还有一个只包含其项目名称的属性:

>>>
>>> print(Color.RED.name)
RED

枚举支持迭代,按定义顺序:

>>>
>>> class Shake(Enum):
...     VANILLA = 7
...     CHOCOLATE = 4
...     COOKIES = 9
...     MINT = 3
...
>>> for shake in Shake:
...     print(shake)
...
Shake.VANILLA
Shake.CHOCOLATE
Shake.COOKIES
Shake.MINT

枚举成员是可清除的,因此可以在字典和集合中使用它们:

>>>
>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True

枚举成员及其属性的编程访问

有时以编程方式访问枚举中的成员很有用(即 Color.RED 不会这样做的情况,因为在编写程序时不知道确切的颜色)。 枚举允许这样的访问:

>>>
>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>

如果要按名称访问枚举成员,请使用项目访问权限:

>>>
>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>

如果你有一个枚举成员并需要它namevalue

>>>
>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1

复制枚举成员和值

拥有两个具有相同名称的枚举成员是无效的:

>>>
>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'SQUARE'

但是,允许两个枚举成员具有相同的值。 给定两个具有相同值的成员 A 和 B(并且 A 首先定义),B 是 A 的别名。按值查找 A 和 B 的值将返回 A。按名称查找 B 也将返回 A:

>>>
>>> class Shape(Enum):
...     SQUARE = 2
...     DIAMOND = 1
...     CIRCLE = 3
...     ALIAS_FOR_SQUARE = 2
...
>>> Shape.SQUARE
<Shape.SQUARE: 2>
>>> Shape.ALIAS_FOR_SQUARE
<Shape.SQUARE: 2>
>>> Shape(2)
<Shape.SQUARE: 2>

注意

不允许尝试创建与已定义属性(另一个成员、方法等)同名的成员或尝试创建与成员同名的属性。

确保唯一的枚举

默认情况下,枚举允许多个名称作为同一值的别名。如果不需要此行为,可以使用以下装饰器来确保每个值在枚举中只使用一次:

@enum.unique

专门用于枚举的类装饰器。 它搜索枚举的 __members__ 收集它找到的任何别名; 如果发现任何 ValueError 并提供详细信息:

>>>
>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
...     ONE = 1
...     TWO = 2
...     THREE = 3
...     FOUR = 3
...
Traceback (most recent call last):
...
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

使用自动值

如果确切的值不重要,您可以使用auto

>>>
>>> from enum import Enum, auto
>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

这些值由 _generate_next_value_() 选择,可以被覆盖:

>>>
>>> class AutoName(Enum):
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

注意

默认 _generate_next_value_() 方法的目标是按顺序提供下一个 int 和提供的最后一个 int,但它这样做的方式是一个实现细节,可能会改变。

迭代

迭代枚举的成员不提供别名:

>>>
>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]

特殊属性 __members__ 是一个有序的字典,将名称映射到成员。 它包括枚举中定义的所有名称,包括别名:

>>>
>>> for name, member in Shape.__members__.items():
...     name, member
...
('SQUARE', <Shape.SQUARE: 2>)
('DIAMOND', <Shape.DIAMOND: 1>)
('CIRCLE', <Shape.CIRCLE: 3>)
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)

__members__ 属性可用于对枚举成员进行详细的编程访问。 例如,查找所有别名:

>>>
>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']

比较

枚举成员按身份进行比较:

>>>
>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True

不支持枚举值之间的有序比较。 枚举成员不是整数(但请参阅下面的 IntEnum):

>>>
>>> Color.RED < Color.BLUE
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'

尽管如此定义了平等比较:

>>>
>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == Color.BLUE
True

与非枚举值的比较将始终比较不相等(同样,IntEnum 被明确设计为表现不同,见下文):

>>>
>>> Color.BLUE == 2
False

允许的成员和枚举属性

上面的示例使用整数作为枚举值。 使用整数既简短又方便(由函数式 API 默认提供),但并未严格执行。 在绝大多数用例中,人们并不关心枚举的实际值是多少。 但如果值很重要,枚举可以有任意值。

枚举是 Python 类,可以像往常一样具有方法和特殊方法。 如果我们有这个枚举:

>>>
>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self is the member here
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls here is the enumeration
...         return cls.HAPPY
...

然后:

>>>
>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'

允许的规则如下:以单个下划线开头和结尾的名称是枚举保留的,不能使用; 枚举内定义的所有其他属性都将成为该枚举的成员,但特殊方法(__str__()、__add__() 等)、描述符(方法也是描述符)和_ignore_中列出的变量名除外。

注意:如果您的枚举定义了 __new__() 和/或 __init__() ,那么为枚举成员提供的任何值都将被传递到这些方法中。

受限制的枚举子类

一个新的 Enum 类必须有一个 Enum 基类,最多一种具体数据类型,以及所需数量的基于对象的混合类。 这些基类的顺序是:

def EnumName([mix-in, ...,] [data-type,] base-enum):
    pass

此外,仅当枚举未定义任何成员时,才允许子类化枚举。所以这是禁止的:

>>>
>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: Cannot extend enumerations

但这是允许的:

>>>
>>> class Foo(Enum):
...     def some_behavior(self):
...         pass
...
>>> class Bar(Foo):
...     HAPPY = 1
...     SAD = 2
...

允许对定义成员的枚举进行子类化会导致违反某些类型和实例的重要不变量。另一方面,允许在一组枚举之间共享一些共同行为是有意义的。(有关示例,请参阅OrderedEnum。)

Pickling

枚举可以被 pickle 和 unpickled:

>>>
>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True

pickling 的通常限制适用:picklable 枚举必须在模块的顶层定义,因为 unpickling 要求它们可以从该模块导入。

注意

使用 pickle 协议版本 4,可以轻松地 pickle 嵌套在其他类中的枚举。

可以通过在枚举类中定义 __reduce_ex__() 来修改枚举成员的 pickled/unpickled 方式。

功能API

Enum 类是可调用的,提供以下功能 API:

>>>
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> Animal.ANT.value
1
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

此 API 的语义类似于 namedtuple。 调用 Enum 的第一个参数是枚举的名称。

第二个参数是枚举成员名称的来源。 它可以是以空格分隔的名称字符串、名称序列、具有键/值对的二元组序列或名称到值的映射(例如字典)。 最后两个选项允许为枚举分配任意值; 其他自动分配从 1 开始的递增整数(使用 start 参数指定不同的起始值)。 返回从 Enum 派生的新类。 换句话说,上面对 Animal 的赋值等同于:

>>>
>>> class Animal(Enum):
...     ANT = 1
...     BEE = 2
...     CAT = 3
...     DOG = 4
...

默认为 1 而不是 0 作为起始数字的原因是 0 在布尔意义上是 False,但枚举成员都评估为 True。

使用函数式 API 创建的 Pickling 枚举可能很棘手,因为框架堆栈实现细节用于尝试找出枚举是在哪个模块中创建的(例如,如果您在单独的模块中使用实用程序函数,它将失败,并且也可能无法工作 在 IronPython 或 Jython 上)。 解决方案是显式指定模块名称,如下所示:

>>>
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)

警告

如果未提供模块,并且 Enum 无法确定它是什么,则新的 Enum 成员将不可取消; 为了使错误更接近源头,酸洗将被禁用。

在某些情况下,新的 pickle 协议 4 还依赖于将 __qualname__ 设置为 pickle 能够找到类的位置。 例如,如果该类在全局范围内的类 SomeData 中可用:

>>>
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')

完整的签名是:

Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
值:

新的Enum类将作为其名称记录。

名称:

Enum成员。这可以是空格或逗号分隔的字符串(除非另有说明,否则值将从1开始):

'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'

或者名称的迭代器:

['RED', 'GREEN', 'BLUE']

或(名称,值)对的迭代器:

[('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]

或映射:

{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
模块:

可以找到新Enum类的模块名称。

qualname:

在模块中可以找到新的Enum类。

类型:

键入以混合到新的Enum类。

开始:

如果只传入名称,则开始计数的数字。

在3.5版本中更改:启动加入参数。

派生枚举IntEnum

提供的 Enum 的第一个变体也是 int 的子类。 IntEnum 的成员可以与整数进行比较; 通过扩展,不同类型的整数枚举也可以相互比较:

>>>
>>> from enum import IntEnum
>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Request(IntEnum):
...     POST = 1
...     GET = 2
...
>>> Shape == 1
False
>>> Shape.CIRCLE == 1
True
>>> Shape.CIRCLE == Request.POST
True

但是,它们仍然无法与标准Enum枚举进行比较:

>>>
>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...
>>> Shape.CIRCLE == Color.RED
False

IntEnum 值在您期望的其他方面表现得像整数:

>>>
>>> int(Shape.CIRCLE)
1
>>> ['a', 'b', 'c'][Shape.CIRCLE]
'b'
>>> [i for i in range(Shape.SQUARE)]
[0, 1]

INTFLAG

提供的下一个 Enum 变体 IntFlag 也是基于 int 的。 不同之处在于 IntFlag 成员可以使用按位运算符(&、|、^、~)组合,结果仍然是 IntFlag 成员。 然而,顾名思义,IntFlag 成员也是 int 的子类,并且可以在任何使用 int 的地方使用。 除了按位操作之外,对 IntFlag 成员的任何操作都将失去 IntFlag 成员资格。

3.6 版中的新功能。

示例 IntFlag 类:

>>>
>>> from enum import IntFlag
>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...
>>> Perm.R | Perm.W
<Perm.R|W: 6>
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
>>> Perm.R in RW
True

也可以命名组合:

>>>
>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...     RWX = 7
>>> Perm.RWX
<Perm.RWX: 7>
>>> ~Perm.RWX
<Perm.-8: -8>

IntFlag和之间的另一个重要区别Enum是,如果没有设置标志(值为0),它的布尔评估是False

>>>
>>> Perm.R & Perm.X
<Perm.0: 0>
>>> bool(Perm.R & Perm.X)
False

因为IntFlag成员也是int它们的子类,所以它们可以与它们结合使用:

>>>
>>> Perm.X | 8
<Perm.8|X: 9>

Flag

最后一个变体是 Flag。 与 IntFlag 一样,Flag 成员可以使用按位运算符(&、|、^、~)组合。 与 IntFlag 不同,它们不能与任何其他 Flag 枚举或 int 组合,也不能与之比较。 虽然可以直接指定值,但建议使用 auto 作为值并让 Flag 选择适当的值。

3.6 版中的新功能。

与 IntFlag 一样,如果 Flag 成员的组合没有设置任何标志,则布尔值评估为 False:

>>>
>>> from enum import Flag, auto
>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color.0: 0>
>>> bool(Color.RED & Color.GREEN)
False

单个标志的值应为2的幂(1,2,4,8,…),而标志的组合则不会:

>>>
>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...     WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
<Color.WHITE: 7>

为“无标志设置”条件赋予名称不会更改其布尔值:

>>>
>>> class Color(Flag):
...     BLACK = 0
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.BLACK
<Color.BLACK: 0>
>>> bool(Color.BLACK)
False

注意

对于大多数新代码,强烈建议使用 Enum 和 Flag,因为 IntEnum 和 IntFlag 打破了枚举的一些语义承诺(通过与整数进行比较,从而通过传递给其他不相关的枚举)。 IntEnum 和 IntFlag 只应在 Enum 和 Flag 不起作用的情况下使用; 例如,当整数常量被枚举替换时,或者为了与其他系统的互操作性。

其他

虽然 IntEnum 是枚举模块的一部分,但独立实现起来非常简单:

class IntEnum(int, Enum):
    pass

这演示了如何定义相似的派生枚举; 例如,混合在 str 而不是 int 中的 StrEnum。

一些规则:

  1. 当对 Enum 进行子类化时,混合类型必须出现在基序列中的 Enum 本身之前,如上面的 IntEnum 示例所示。
  2. 虽然 Enum 可以有任何类型的成员,但是一旦你混合了一个额外的类型,所有的成员都必须有那个类型的值,例如 上面的整数。 此限制不适用于仅添加方法且未指定其他数据类型(如 int 或 str)的混合。
  3. 当混合了另一种数据类型时,value 属性与枚举成员本身不同,尽管它是等价的并且比较相等。
  4. %风格的格式化:%s和%r分别调用了Enum类的__str__()和__repr__(); 其他代码(例如 IntEnum 的 %i 或 %h)将枚举成员视为其混合类型。
  5. 格式化字符串文字、str.format() 和 format() 将使用混合类型的 __format__()。 如果需要 Enum 类的 str() 或 repr(),请使用 !s 或 !r 格式代码。

有趣的例子

虽然 Enum、IntEnum、IntFlag 和 Flag 有望涵盖大多数用例,但它们无法涵盖所有用例。 以下是一些可以直接使用的不同类型枚举的方法,或者作为创建自己的枚举的示例。

省略值

在许多用例中,人们并不关心枚举的实际值是多少。 有几种方法可以定义这种类型的简单枚举:

  • 使用auto值的实例
  • 使用实例object作为值
  • 使用描述性字符串作为值
  • 使用元组作为值,使用自定义 __new__() 将元组替换为 int 值

使用这些方法中的任何一种都向用户表明这些值并不重要,并且还使用户能够添加、删除或重新排序成员而不必对其余成员重新编号。

无论您选择哪种方法,您都应该提供一个 repr() 来隐藏(不重要的)值:

>>>
>>> class NoValue(Enum):
...     def __repr__(self):
...         return '<%s.%s>' % (self.__class__.__name__, self.name)
...

使用auto

使用auto看起来像:

>>>
>>> class Color(NoValue):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN>

使用object

使用object看起来像:

>>>
>>> class Color(NoValue):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN>

使用描述性字符串

使用字符串作为值将如下所示:

>>>
>>> class Color(NoValue):
...     RED = 'stop'
...     GREEN = 'go'
...     BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
'go'

使用自定义__new__()

使用自动编号__new__()看起来像:

>>>
>>> class AutoNumber(NoValue):
...     def __new__(cls):
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...
>>> class Color(AutoNumber):
...     RED = ()
...     GREEN = ()
...     BLUE = ()
...
>>> Color.GREEN
<Color.GREEN>
>>> Color.GREEN.value
2

注意

__new__() 方法(如果已定义)在创建 Enum 成员期间使用; 然后它被 Enum 的 __new__() 替换,后者在类创建后用于查找现有成员。

OrderedEnum

一个不基于 IntEnum 的有序枚举,因此保持了正常的 Enum 不变量(例如不能与其他枚举相比较):

>>>
>>> class OrderedEnum(Enum):
...     def __ge__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value >= other.value
...         return NotImplemented
...     def __gt__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value > other.value
...         return NotImplemented
...     def __le__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value <= other.value
...         return NotImplemented
...     def __lt__(self, other):
...         if self.__class__ is other.__class__:
...             return self.value < other.value
...         return NotImplemented
...
>>> class Grade(OrderedEnum):
...     A = 5
...     B = 4
...     C = 3
...     D = 2
...     F = 1
...
>>> Grade.C < Grade.A
True

DuplicateFreeEnum

如果找到重复的成员名称而不是创建别名,则会引发错误:

>>>
>>> class DuplicateFreeEnum(Enum):
...     def __init__(self, *args):
...         cls = self.__class__
...         if any(self.value == e.value for e in cls):
...             a = self.name
...             e = cls(self.value).name
...             raise ValueError(
...                 "aliases not allowed in DuplicateFreeEnum:  %r --> %r"
...                 % (a, e))
...
>>> class Color(DuplicateFreeEnum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3
...     GRENE = 2
...
Traceback (most recent call last):
...
ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'

注意

这是继承Enum以添加或更改其他行为以及禁止别名的有用示例。如果唯一需要的更改是禁止别名,则unique()可以使用装饰器。

Planet

如果定义了 __new__() 或 __init__() ,枚举成员的值将传递给这些方法:

>>>
>>> class Planet(Enum):
...     MERCURY = (3.303e+23, 2.4397e6)
...     VENUS   = (4.869e+24, 6.0518e6)
...     EARTH   = (5.976e+24, 6.37814e6)
...     MARS    = (6.421e+23, 3.3972e6)
...     JUPITER = (1.9e+27,   7.1492e7)
...     SATURN  = (5.688e+26, 6.0268e7)
...     URANUS  = (8.686e+25, 2.5559e7)
...     NEPTUNE = (1.024e+26, 2.4746e7)
...     def __init__(self, mass, radius):
...         self.mass = mass       # in kilograms
...         self.radius = radius   # in meters
...     @property
...     def surface_gravity(self):
...         # universal gravitational constant  (m3 kg-1 s-2)
...         G = 6.67300E-11
...         return G * self.mass / (self.radius * self.radius)
...
>>> Planet.EARTH.value
(5.976e+24, 6378140.0)
>>> Planet.EARTH.surface_gravity
9.802652743337129

TIMEPERIOD

显示正在使用的 _ignore_ 属性的示例:

>>>
>>> from datetime import timedelta
>>> class Period(timedelta, Enum):
...     "different lengths of time"
...     _ignore_ = 'Period i'
...     Period = vars()
...     for i in range(367):
...         Period['day_%d' % i] = i
...
>>> list(Period)[:2]
[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
>>> list(Period)[-2:]
[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]

Enum与众不同?

枚举有一个自定义元类,它影响派生的枚举类及其实例(成员)的许多方面。

枚举类

EnumMeta 元类负责提供 __contains__()、__dir__()、__iter__() 和其他方法,这些方法允许使用 Enum 类执行在典型类上失败的操作,例如 list(Color) 或 Color 中的 some_enum_var。 EnumMeta 负责确保最终 Enum 类上的各种其他方法是正确的(例如 __new__()、__getnewargs__()、__str__() 和 __repr__())。

枚举成员(又名实例)

Enum 成员最有趣的地方在于它们是单例。 EnumMeta 在创建 Enum 类本身时创建它们,然后放置一个自定义的 __new__() 以确保不会通过仅返回现有成员实例来实例化新的成员实例。

更精细的点

支持的__dunder__名称

__members__ 是 member_name:member 项的 OrderedDict。 它仅在课堂上可用。

__new__(),如果指定,必须创建并返回枚举成员; 适当地设置成员的_value_也是一个很好的主意。 一旦创建了所有成员,就不再使用它。

支持的_sunder_名称

  • _name_ – 会员姓名
  • _value_ – 会员价值; 可以设置/修改__new__
  • _missing_ – 未找到值时使用的查找函数; 可能会被覆盖
  • _ignore_– 作为a list()或a 的名称列表,str()不会转换为成员,并将从最终的类中删除
  • _order_ – 在Python 2/3代码中使用以确保成员顺序一致(类属性,在类创建期间删除)
  • _generate_next_value_-所用的功能性API,并通过 auto获得用于枚举成员一个适当的值; 可能会被覆盖

新的3.6版:_missing__order__generate_next_value_

版本3.7中的新功能: _ignore_

为了帮助保持Python 2 / Python 3代码同步,_order_可以提供属性。它将根据枚举的实际顺序进行检查,如果两者不匹配则引发错误:

>>>
>>> class Color(Enum):
...     _order_ = 'RED GREEN BLUE'
...     RED = 1
...     BLUE = 3
...     GREEN = 2
...
Traceback (most recent call last):
...
TypeError: member order does not match _order_

注意

在Python 2代码中,_order_属性是必需的,因为定义顺序在被记录之前就丢失了。

Enum成员类型

Enum成员是他们Enum班级的实例,通常作为EnumClass.member。在某些情况下,它们也可以被访问EnumClass.member.member,但是你永远不应该这样做,因为查找可能会失败,或者更糟糕的是,返回Enum你正在寻找的成员之外的东西 (这是为成员使用全大写名称的另一个好理由):

>>>
>>> class FieldTypes(Enum):
...     name = 0
...     value = 1
...     size = 2
...
>>> FieldTypes.value.size
<FieldTypes.size: 2>
>>> FieldTypes.size.value
2

版本3.5已更改。

Enum类和成员的布尔值

Enum了与非混合成员Enum类型(如 intstr等)是根据评估的混合型的规则; 否则,所有成员评估为True。要使您自己的Enum的布尔值评估取决于成员的值,请将以下内容添加到您的类中:

def __bool__(self):
    return bool(self.value)

Enum课程总是评估为True

Enum带方法的类

如果你给你的Enum子类额外的方法,比如 上面的Planet类,那些方法将显示在dir()成员的一个中,但不会出现在类中:

>>>
>>> dir(Planet)
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value']

结合成员Flag

如果未命名Flag成员的组合,repr()则将包括所有命名标志和值中的所有已命名标志组合:

>>>
>>> class Color(Flag):
...     RED = auto()
...     GREEN = auto()
...     BLUE = auto()
...     MAGENTA = RED | BLUE
...     YELLOW = RED | GREEN
...     CYAN = GREEN | BLUE
...
>>> Color(3)  # named combination
<Color.YELLOW: 3>
>>> Color(7)      # not named combination
<Color.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>