enum ---支持枚举

3.4 新版功能.

源代码: Lib/enum.py


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

注解

枚举成员的情况

因为枚举用于表示常量,所以我们建议对枚举成员使用大写字母的名称,并且将在我们的示例中使用这种样式。

模块内容

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

class enum.Enum

用于创建枚举常量的基类。参见第节 Functional API 用于替代结构语法。

class enum.IntEnum

用于创建枚举常量的基类,这些常量也是 int .

class enum.StrEnum

用于创建枚举常量的基类,这些常量也是 str .

class enum.IntFlag

用于创建枚举常量的基类,这些枚举常量可以使用位运算符组合,而不会丢失它们的 IntFlag 会员。 IntFlag 成员也是 int .

class enum.Flag

用于创建枚举常量的基类,这些枚举常量可以使用按位运算组合,而不会丢失它们的 Flag 会员。

enum.unique()

确保只有一个名称绑定到任何一个值的枚举类修饰符。

class enum.auto

实例将替换为枚举成员的相应值。 StrEnum 默认为成员名称的小写版本,而其他枚举默认为1并从那里递增。

3.6 新版功能: Flag, IntFlag, auto

3.10 新版功能: StrEnum

创建枚举

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

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

注解

枚举成员值

成员值可以是任何值: intstr 等。如果精确值不重要,则可以使用 auto 将为您选择实例和适当的值。混合时必须小心 auto 其他值。

注解

命名法

  • Color 是一个 枚举 (或) enum

  • 属性 Color.REDColor.GREEN 等等,都是 枚举成员 (或) 枚举成员 )和在功能上是常量。

  • 枚举成员具有 姓名价值观 (名字) Color.REDRED 的价值 Color.BLUE3 等)

注解

即使我们使用 class 语法创建枚举时,枚举不是普通的Python类。见 How are Enums different? 了解更多详细信息。

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

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

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

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

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

>>> 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: 'SQUARE' already defined as: 2

但是,允许两个枚举成员具有相同的值。如果两个成员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

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

注解

违约的目标 _generate_next_value_() 方法是提供下一个 int 按顺序与最后一个 int 提供了,但其实现方式是一个实现细节,可能会更改。

注解

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

迭代

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

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

枚举值之间的有序比较是 not 支持。枚举成员不是整数(但请参见 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 ,但不严格执行。在绝大多数用例中,我们不关心枚举的实际值是什么。但如果价值 is 重要的是,枚举可以具有任意值。

枚举是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__() 然后,给枚举成员的任何值都将传递到这些方法中。见 Planet 举个例子。

受限枚举子类化

一个新的 Enum 类必须有一个基本枚举类,最多一个具体的数据类型,以及尽可能多的 object -根据需要基于mixin类。这些基类的顺序是:

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

此外,只有枚举没有定义任何成员时,才允许对枚举进行子类化。所以这是禁止的:

>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: MoreColor: cannot extend enumeration 'Color'

但这是允许的:

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

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

Pickling

可以对枚举进行pickle和unpickle::

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

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

注解

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

可以通过定义 __reduce_ex__() 在枚举类中。

功能性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 是枚举的名称。

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

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

违约原因 1 作为起始编号而不是 0 那是 0False 在布尔值意义上,但枚举成员都计算为 True .

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

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

警告

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

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

新枚举类将记录什么作为其名称。

姓名

枚举成员。这可以是空白或逗号分隔的字符串(除非另有规定,否则值将从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}
模块

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

质量名称

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

类型

键入以混合到新枚举类中。

开始

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

在 3.5 版更改: 这个 开始 已添加参数。

派生枚举

IntEnum

第一个变化 Enum 它也是 int . AN成员 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]

StrEnum

第二个变体 Enum 它也是 str . a的成员 StrEnum 可以与字符串进行比较;通过扩展,不同类型的字符串枚举也可以相互比较。 StrEnum 存在以帮助避免获取错误成员的问题:

>>> from enum import StrEnum
>>> class Directions(StrEnum):
...     NORTH = 'north',    # notice the trailing comma
...     SOUTH = 'south'

之前 StrEnumDirections.NORTH 会是 tuple ('north',) .

注解

与其他枚举不同, str(StrEnum.member) 将返回成员的值而不是通常的 "EnumClass.member" .

3.10 新版功能.

IntFlag

的下一个变种 Enum 只要, IntFlag ,也是基于 int 。不同之处在于 IntFlag 可以使用按位运算符(&、|、^、~)组合成员,并且结果仍然是 IntFlag 如果可能的话,请议员发言。然而,顾名思义, IntFlag 成员也是子类别 int 并且可以在任何地方使用 int 是使用的。

注解

对一个对象执行的任何操作 IntFlag 除按位操作之外的成员将丢失 IntFlag 会员资格。

注解

导致无效的按位操作 IntFlag 值将丢失 IntFlag 会员资格。

3.6 新版功能.

在 3.10 版更改.

样品 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: 0>
>>> Perm(7)
<Perm.RWX: 7>

注解

命名组合被视为别名。别名在迭代期间不会显示,但可以从按值查找中返回。

在 3.10 版更改.

另一个重要区别是 IntFlagEnum 如果没有设置任何标志(值为0),则其布尔值为 False ::

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

因为 IntFlag 成员也是 int 它们可以与它们合并(但可能会失败 IntFlag 成员::

>>> Perm.X | 4
<Perm.R|X: 5>

>>> Perm.X | 8
9

注解

否定运算符, ~ ,则始终返回一个 IntFlag 具有正值的成员::

>>> (~Perm.X).value == (Perm.R|Perm.W).value == 6
True

IntFlag 成员也可以迭代:

>>> list(RW)
[<Perm.R: 4>, <Perm.W: 2>]

3.10 新版功能.

最后一个变化是 Flag . 类似于 IntFlagFlag 可以使用位运算符(&、、^、~)组合成员。不像 IntFlag ,它们不能与任何其他项组合,也不能与任何其他项进行比较。 Flag 枚举,nor 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>
>>> 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>

为“no flags set”条件命名不会更改其布尔值:

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

Flag 成员也可以迭代:

>>> purple = Color.RED | Color.BLUE
>>> list(purple)
[<Color.RED: 1>, <Color.BLUE: 2>]

3.10 新版功能.

注解

对于大多数新代码, EnumFlag 强烈建议,因为 IntEnumIntFlag 打破枚举的一些语义承诺(通过与整数比较,从而通过传递到其他不相关的枚举)。 IntEnumIntFlag 仅在以下情况下使用 EnumFlag 不会这样做;例如,当整数常量被枚举替换时,或者为了与其他系统的互操作性。

其他

同时 IntEnumenum 模块,独立实现非常简单:

class IntEnum(int, Enum):
    pass

这演示了如何定义类似的派生枚举;例如 StrEnum 混入 str 而不是 int .

一些规则:

  1. 子类化时 Enum ,混合类型必须出现在 Enum 以碱基的顺序,如 IntEnum 上面的例子。

  2. 同时 Enum 可以有任何类型的成员,一旦混合到其他类型中,所有成员都必须具有该类型的值,例如。 int 上面。此限制不适用于只添加方法而不指定其他类型的混合插件。

  3. 当混合了另一种数据类型时, value 属性是 不一样 作为枚举成员本身,尽管它是等效的,并且将比较相等。

  4. %-样式格式: %s and %r call the Enum class's __str__() __repr__() 分别;其他代码(如 %i%h 对于intenum)将枚举成员视为其混合类型。

  5. Formatted string literalsstr.format()format() 将使用混合类型的 __format__() 除非 __str__()__format__() 在子类中重写,在这种情况下,重写的方法或 Enum 将使用方法。使用!S和!R格式代码强制使用 Enum 班的 __str__()__repr__() 方法。

何时使用 __new__() VS __init__()

__new__() 必须在要自定义 Enum 成员。任何其他修改都可以 __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

有趣的例子

同时 EnumIntEnumIntFlagFlag 预计将覆盖大多数用例,它们不能覆盖所有用例。以下是一些不同类型的枚举的方法,可以直接使用,也可以作为创建自己的枚举的示例。

省略值

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

  • 使用的实例 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

为了达到一个更普遍的目的 AutoNumber ,添加 *args 签字人:

>>> class AutoNumber(NoValue):
...     def __new__(cls, *args):      # this is the only change from above
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...

当你继承 AutoNumber 你可以自己写 __init__ 要处理任何额外的参数:

>>> class Swatch(AutoNumber):
...     def __init__(self, pantone='unknown'):
...         self.pantone = pantone
...     AUBURN = '3497'
...     SEA_GREEN = '1246'
...     BLEACHED_CORAL = () # New color, no Pantone code yet!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'

注解

这个 __new__() 方法(如果已定义)在创建枚举成员期间使用;然后用枚举的 __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'

注解

这是一个很有用的例子,可以通过子类化枚举来添加或更改其他行为,以及禁止别名。如果唯一需要的更改是不允许使用别名,则 unique() 可以改用decorator。

行星

如果 __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)>]

枚举有什么不同?

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

枚举类

这个 EnumMeta 元类负责提供 __contains__()__dir__()__iter__() 以及其他可以让人用 Enum 在典型类上失败的类,例如 list(Color) or some_enum_var in Color. EnumMeta is responsible for ensuring that various other methods on the final Enum class are correct (such as _ _new__(), __getnewargs__()__str__()__repr__()

枚举成员(aka实例)

枚举成员最有趣的一点是它们是单例的。 EnumMeta 在创建 Enum 类本身,然后将 __new__() 通过只返回现有的成员实例来确保没有新的实例被实例化。

Finer点

支持 __dunder__ 姓名

__members__ 是的只读顺序映射 member_name:member 项目。它只在课堂上提供。

__new__() 如果指定,则必须创建并返回枚举成员;设置成员的 _value_ 适当地。创建所有成员后,将不再使用它。

支持 _sunder_ 姓名

  • _name_ --成员的姓名

  • _value_ -- value of the member; can be set / modified in __new__

  • _missing_ --找不到值时使用的查找函数;可以重写

  • _ignore_ --姓名列表,可以是 list 或A str ,不会转换为成员,将从最终类中移除

  • _order_ --在python 2/3代码中使用,以确保成员顺序一致(class属性,在类创建期间移除)

  • _generate_next_value_ --由 Functional API 并且通过 auto 为枚举成员获取适当的值;可以重写

注解

对于标准配置 Enum 类选择的下一个值是看到的最后一个加1的值。

Flag -类型类选择的下一个值将是下一个最高的2次幂,而不考虑最后看到的值。

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_:
['RED', 'BLUE', 'GREEN']
['RED', 'GREEN', 'BLUE']

注解

在python 2中,对 _order_ 属性是必需的,因为定义顺序在记录之前已丢失。

_Private__names

专用名称不会转换为枚举成员,但会保留普通属性。

在 3.10 版更改.

Enum 成员类型

Enum 成员是其 Enum 类,并且通常作为 EnumClass.member 。在Python版本中 3.53.9 您可以从其他成员访问成员--不鼓励这种做法,并且在 3.10 Enum 又回到了不允许它的状态::

>>> class FieldTypes(Enum):
...     name = 0
...     value = 1
...     size = 2
...
>>> FieldTypes.value.size
Traceback (most recent call last):
...
AttributeError: FieldTypes: no attribute 'size'

在 3.5 版更改.

在 3.10 版更改.

创建与其他数据类型混合的成员

当子类化其他数据类型时,例如 intstr ,带有 Enum ,后面的所有值 = 传递给该数据类型的构造函数。例如::

>>> class MyEnum(IntEnum):
...     example = '11', 16      # '11' will be interpreted as a hexadecimal
...                             # number
>>> MyEnum.example
<MyEnum.example: 17>

布尔值 Enum 类和成员

Enum 与非 -Enum 类型(如 intstr 等)根据混合类型的规则进行计算;否则,所有成员的计算结果为 True . 若要使自己的枚举的布尔值取决于成员的值,请将以下内容添加到类中:

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__', 'mass', 'name', 'radius', 'surface_gravity', 'value']

组合的成员 Flag

迭代Flag成员的组合将仅返回由单个位组成的成员::

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

StrEnum and str.__str__()

两者之间的一个重要区别 StrEnum 其他枚举是 __str__() 方法;因为 StrEnum 成员是字符串,Python的某些部分将直接读取字符串数据,而其他部分将调用 str() . 为了使这两个操作有相同的结果, StrEnum.__str__() 将与 str.__str__() 以便 str(StrEnum.member) == StrEnum.member 是真的。

FlagIntFlag 细节点

代码示例::

>>> class Color(IntFlag):
...     BLACK = 0
...     RED = 1
...     GREEN = 2
...     BLUE = 4
...     PURPLE = RED | BLUE
...     WHITE = RED | GREEN | BLUE
...
  • 单位标志是规范的

  • 多位和零位标志是别名

  • 迭代期间仅返回规范标志::

    >>> list(Color.WHITE)
    [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
    
  • 否定标志或标志集将返回具有相应正整数值的新标志/标志集::

    >>> Color.GREEN
    <Color.GREEN: 2>
    
    >>> ~Color.GREEN
    <Color.PURPLE: 5>
    
  • 伪标志的名称由其成员名称构成::

    >>> (Color.RED | Color.GREEN).name
    'RED|GREEN'
    
  • 多位标志(也称为别名)可以从以下操作返回::

    >>> Color.RED | Color.BLUE
    <Color.PURPLE: 5>
    
    >>> Color(7)  # or Color(-1)
    <Color.WHITE: 7>
    
  • 成员身份/包容检查已稍有更改-从不将零值标志视为包含::

    >>> Color.BLACK in Color.WHITE
    False
    

    否则,如果一个标志的所有位都在另一个标志中,则返回True::

    >>> Color.PURPLE in Color.WHITE
    True
    

有一种新的边界机制可以控制如何处理超出范围/无效的位: STRICTCONFORMEJECT ,以及 KEEP

  • Strict-->在显示无效值时引发异常

  • Conform-->丢弃任何无效位

  • 弹出-->丢失标志状态,变成具有给定值的正常整型

  • 保留-->保留多余的位
    • 保留标志状态和额外位

    • 多余的位不会在迭代中显示

    • 额外的位确实会出现在repr()和str()中

标志的默认值为 STRICT ,默认设置为 IntFlagDISCARD ,和的默认设置为 _convert_KEEP (请参阅 ssl.Options 有关以下情况的示例,请参阅 KEEP 是必需的)。