python / 3.7.2rc1 / all / library-enum.html

枚举—支持枚举

3.4 版的新Function。

源代码: Lib/enum.py


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

Module Contents

该模块定义了四个枚举类,可用于定义唯一的名称和值集:EnumIntEnumFlagIntFlag。它还定义了一个装饰器unique()和一个辅助器auto

  • 类别 enum. Enum

    • 用于创建枚举常量的 Base Class。有关替代的构造语法,请参见第Functional API节。
  • 类别 enum. IntEnum

    • 用于创建枚举常量的 Base Class,这些常量也是int的子类。
  • 类别 enum. IntFlag

    • 用于创建枚举常量的 Base Class,可以使用按位运算符将其组合在一起而不会失去其IntFlag成员资格。 IntFlag成员也是int的子类。
  • 类别 enum. Flag

    • 用于创建枚举常量的 Base Class,这些常量可以使用按位运算进行组合而不会失去其Flag成员资格。
  • enum. unique ( )

    • 枚举类装饰器,可确保仅将一个名称绑定到任何一个值。
  • 类别 enum. auto

    • 实例将替换为 Enum 成员的适当值。初始值从 1 开始。

3.6 版的新Function:FlagIntFlagauto

创建枚举

枚举使用class语法创建,这使得它们易于读取和写入。另一种创建方法在Functional API中描述。要定义枚举,请子类Enum如下:

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

Note

枚举成员值

成员值可以是intstr等等。如果确切的值不重要,则可以使用auto实例,并会为您选择一个适当的值。如果将auto与其他值混合使用,则必须小心。

Note

Nomenclature

  • Color类是枚举(或* enum *)

  • 属性Color.REDColor.GREEN等是枚举成员(或枚举成员),并且是Function常数。

  • 枚举成员具有* names values *(Color.RED的名称为REDColor.BLUE的值为3等)。

Note

即使我们使用class语法创建枚举,但枚举也不是普通的 Python 类。有关更多详细信息,请参见枚举有什么不同?

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

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

…虽然他们的repr拥有更多信息:

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

枚举成员的* type *是它所属的枚举:

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

枚举成员还具有仅包含其项目名称的属性:

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

枚举支持按定义 Sequences 进行迭代:

>>> 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不会这样做)。 Enum允许这样的访问:

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

如果要按* name *访问枚举成员,请使用项访问:

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

Note

不允许try使用与已经定义的属性相同的名称创建成员(另一个成员,方法等),或者try创建与成员相同名称的属性。

确保唯一的枚举值

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

  • @ enum. unique

专门用于枚举的class装饰器。它搜索枚举的__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'>]

Note

默认_generate_next_value_()方法的目标是依次提供下一个int和最后提供的int,但是它的实现方式是实现细节,并且可能会发生变化。

Note

必须在任何成员之前定义_generate_next_value_()方法。

Iteration

遍历枚举的成员不提供别名:

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

特殊属性__members__是名称到成员的只读有序 Map。它包括枚举中定义的所有名称,包括别名:

>>> 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']

Comparisons

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

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

允许的枚举成员和属性

上面的示例将整数用作枚举值。使用整数既方便又快捷(默认情况下由Functional 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
...

Then:

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

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

注意:如果您的枚举定义了new()和/或init(),则赋予枚举成员的任何值都将传递到这些方法中。有关示例,请参见Planet

受限制的枚举子类化

新的Enum类必须具有一个基本的 Enum 类,最多一种具体的数据类型以及所需的基于object的混合类。这些 Base Class 的 Sequences 为:

class 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

枚举可以腌制和不腌制:

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

适用于 Pickling 的通常限制:可酸 Eclipse 的枚举必须在模块的顶层定义,因为解酸要求它们可从该模块导入。

Note

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

pass在枚举类中定义reduce_ex(),可以修改如何对 Enum 成员进行 Pickling/Pickling。

Functional API

Enum类是可调用的,提供以下Function性 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的第一个参数是枚举的名称。

第二个参数是枚举成员名称的“来源”。它可以是由空格分隔的名称字符串,名称序列,带有键/值对的 2Tuples 序列或名称到值的 Map(例如字典)。最后两个选项可为枚举分配任意值;其他自动分配以 1 开头的递增整数(使用start参数指定其他起始值)。返回从Enum派生的新类。换句话说,对Animal的上述分配等效于:

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

默认为1作为起始编号而不是0的原因是0在布尔意义上是False,但是枚举成员的所有值均为True

使用Function性 API 创建的 Pickling 枚举可能很棘手,因为使用框架堆栈实现详细信息来try找出要在哪个模块中创建枚举(例如,如果您在单独的模块中使用 Util Function,它将失败.在 IronPython 或 Jython 上)。解决方案是显式指定模块名称,如下所示:

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

Warning

如果未提供module,并且 Enum 无法确定它是什么,新的 Enum 成员将不会被取消;为了使错误更接近源,将禁用 Pickling。

在某些情况下,新的 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)
  • value

    • 新的 Enum 类将记录为其名称的内容。
  • names

    • 枚举成员。可以是空格或逗号分隔的字符串(除非另有说明,否则值将从 1 开始):
'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'

或名称的迭代器:

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

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

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

或 Map:

{'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
  • module

    • 可以在其中找到新的 Enum 类的模块的名称。
  • qualname

    • 在模块中可以找到新的 Enum 类的位置。
  • type

    • 键入以混入新的 Enum 类。
  • start

    • 如果仅传入名称,则从该数字开始计数。

在版本 3.5 中更改:添加了* start *参数。

Derived Enumerations

IntEnum

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

>>> 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 版的新Function。

samplesIntFlag类:

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

IntFlagEnum之间的另一个重要区别是,如果未设置标志(值为 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 版的新Function。

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

Note

对于大多数新代码,强烈建议使用EnumFlag,因为IntEnumIntFlag破坏了枚举的一些语义承诺(与整数比较,因此对其他不相关的枚举具有可传递性)。 IntEnumIntFlag仅在EnumFlag不会使用的情况下才应使用;例如,当整数常量被枚举替换时,或与其他系统互操作时。

Others

尽管IntEnumenum模块的一部分,但独立实现非常简单:

class IntEnum(int, Enum):
    pass

这说明了如何定义相似的派生枚举。例如StrEnum混入str而不是int

Some rules:

  • 当对Enum进行子类化时,混合类型必须按照基数 Sequences 出现在Enum本身之前,如上面的IntEnum示例所示。

  • Enum可以具有任何类型的成员,但是一旦您混合使用其他类型,所有成员都必须具有该类型的值,例如上面int。此限制不适用于仅添加方法且未指定intstr之类的其他数据类型的混入。

  • 当混入另一种数据类型时,value属性与枚举成员本身不是“相同”,尽管它是等效的并且将比较相等。

  • %样式格式:%s 和%r 分别调用Enum类的str()repr();其他代码(例如 IntEnum 的%i 或%h)将枚举成员视为其混合类型。

  • 格式化的字符串 Literalsstr.format()format()将使用混合类型的format()。如果需要Enum类的str()repr(),请使用!s 或!r 格式代码。

何时使用__new __()和__init __()

每当您要自定义Enum成员的实际值时,都必须使用new()。可以使用new()init()进行其他任何修改,最好使用init()

例如,如果您要将多个项目传递给构造函数,但只希望其中一个成为值:

>>> class Coordinate(bytes, Enum):
...     """
...     Coordinate with binary codes that can be indexed by the int code.
...     """
...     def __new__(cls, value, label, unit):
...         obj = bytes.__new__(cls, [value])
...         obj._value_ = value
...         obj.label = label
...         obj.unit = unit
...         return obj
...     PX = (0, 'P.X', 'km')
...     PY = (1, 'P.Y', 'km')
...     VX = (2, 'V.X', 'km/s')
...     VY = (3, 'V.Y', 'km/s')
...

>>> print(Coordinate['PY'])
Coordinate.PY

>>> print(Coordinate(3))
Coordinate.VY

Interesting examples

虽然EnumIntEnumIntFlagFlag涵盖了大多数用例,但它们不能涵盖所有用例。这里是一些不同类型的枚举的配方,这些枚举可以直接使用,也可以作为创建自己的示例。

Omitting values

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

  • 使用auto的实例作为值

  • 使用object的实例作为值

  • 使用描述性字符串作为值

  • 使用 Tuples 作为值,并使用自定义new()将 Tuples 替换为int

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

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

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

Using auto

使用auto看起来像:

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

Using 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

Note

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'

Note

这是将 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 Classes

EnumMeta元类负责提供contains()dir()iter()和其他方法,这些方法允许一个人处理Enum类,而该类在典型类上失败,例如 color 中的 list(Color)或 some_enum_var。 EnumMeta负责确保finallyEnum类上的各种其他方法正确(例如new()getnewargs()str()repr())。

枚举成员(又名实例)

关于枚举成员最有趣的事情是他们是 singleton。 EnumMeta会在创建Enum类本身的同时创建它们的全部,然后放置自定义new(),以确保仅返回现有成员实例就不会实例化任何新实例。

Finer Points

支持的__dunder_名称

__members__member_namemember个项目的只读有序 Map。仅在类上可用。

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

支持的_sunder 名称

  • _name_ –成员名称

  • _value_ –会员的价值;可以在__new__中设置/修改

  • _missing_ –未找到值时使用的查找Function;可能被覆盖

  • _ignore_ –列表名称,无论是list()还是str(),都不会转换为成员,并且将从finally类中删除

  • _order_ –在 Python 2/3 代码中使用,以确保成员 Sequences 一致(类属性,在类创建期间删除)

  • _generate_next_value_ –由Functional APIauto用于为枚举成员获取适当的值;可能被覆盖

3.6 版的新Function:_missing__order__generate_next_value_

3.7 版的新Function:_ignore_

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

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

Note

在 Python 2 代码中,_order_属性是必需的,因为在记录之前定义 Sequences 会丢失。

枚举成员类型

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类型(例如intstr等)混合的Enum个成员;否则,所有成员的评估结果均为True。要使自己的 Enum 的布尔值评估取决于成员的值,请在类中添加以下内容:

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

Enum个类的总评估为True

带有方法的枚举类

如果您为Enum子类提供额外的方法,例如上面的Planet类,则这些方法将显示在成员的dir()中,但不在该类的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>