enum — 支持枚举 — Python 文档

来自菜鸟教程
Python/docs/3.9/library/enum
跳转至:导航、​搜索

enum — 支持枚举

3.4 版中的新功能。


源代码: :source:`Lib/enum.py`



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

笔记

枚举成员案例

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


模块内容

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

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

3.6 版新增:FlagIntFlagauto


创建枚举

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

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

笔记

枚举成员值

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


笔记

命名法

  • Color 是一个 枚举 (或 枚举
  • 属性Color.REDColor.GREEN等是枚举成员(或枚举成员),是函数常量。
  • 枚举成员有 namesvaluesColor.RED 的名称是 REDColor.BLUE 的值是 3 等)


笔记

即使我们使用 class 语法来创建 Enum,Enum 也不是普通的 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
>>>

Enum 成员还有一个属性,只包含它们的项目名称:

>>> 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 无法执行的情况,因为在编写程序时不知道确切的颜色)。 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>

笔记

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


确保唯一的枚举值

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

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

笔记

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

枚举值之间的有序比较不受 支持 。 枚举成员不是整数(但请参阅下面的 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
...

然后:

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

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

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


受限枚举子类化

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

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


酸洗

枚举可以被pickled和unpickled:

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

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

笔记

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


可以通过在枚举类中定义 __reduce_ex__() 来修改 Enum 成员的酸洗/取消酸洗方式。


功能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 的原因是 0 在布尔意义上是 False,但枚举成员都评估为 [ X164X]。

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

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

警告

如果没有提供 module,并且 Enum 无法确定它是什么,则新的 Enum 成员将不会是 unpicklable; 为了使错误更接近源头,将禁用酸洗。


在某些情况下,新的 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 类将作为其名称记录的内容。

名字

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

名称

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

类型

键入以混合到新的 Enum 类中。

开始

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

3.5 版更改: 添加了 start 参数。


派生枚举

枚举

提供的 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]

内部标志

Enum 的下一个变体 IntFlag 也是基于 int。 不同之处在于内部标志可以使用按位运算符(&、|、^、~)组合成员,结果仍然是内部标志成员。 然而,顾名思义,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>

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。 喜欢内部标志 , 旗帜可以使用按位运算符(&、|、^、~)组合成员。 与 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

笔记

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


其他

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

class IntEnum(int, Enum):
    pass

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

一些规则:

  1. 当子类化 Enum 时,混合类型必须出现在 Enum 本身之前的碱基序列,如上面的 IntEnum 示例。
  2. 虽然 Enum 可以有任何类型的成员,一旦你混合了一个额外的类型,所有的成员都必须有那个类型的值,例如 int 以上。 此限制不适用于只添加方法而不指定其他类型的混合。
  3. 当混入另一种数据类型时,value 属性与枚举成员本身的 不相同 ,尽管它是等效的并且将比较相等。
  4. % 样式格式: %s 和 %r 分别调用 Enum 类的 __str__()__repr__(); 其他代码(例如 IntEnum 的 %i 或 %h)将枚举成员视为其混合类型。
  5. 格式化字符串文字str.format()format()将使用混合类型的__format__(),除非[ X131X] 或 __format__() 在子类中被覆盖,在这种情况下,将使用被覆盖的方法或 Enum 方法。 使用 !s 和 !r 格式代码强制使用 Enum 类的 __str__()__repr__() 方法。


何时使用 __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

有趣的例子

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

省略值

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

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

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

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

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

使用 自动

使用 auto 看起来像:

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

使用 对象

使用 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: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'

笔记

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


有序枚举

不基于 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

重复自由枚举

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

>>> 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() 装饰器。


星球

如果定义了 __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

时间段

显示正在使用的 _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) 或 some_enum_var in Color。 EnumMeta 负责确保最终的 Enum 类上的各种其他方法是正确的(例如 __new__()__getnewargs__()__str__()__repr__())。


枚举成员(又名实例)

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


更细的点

支持的 __dunder__ 名称

__members__member_name:member 项的只读有序映射。 它仅在课堂上可用。

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


支持的 _sunder_ 名称

  • _name_ – 成员名称
  • _value_ – 成员的价值; 可以在__new__中设置/修改
  • _missing_ – 找不到值时使用的查找函数; 可能会被覆盖
  • _ignore_ – 名称列表,作为 liststr,不会转换为成员,并将从最终类中删除
  • _order_ - 在 Python 2/3 代码中使用,以确保成员顺序一致(类属性,在类创建时移除)
  • _generate_next_value_Functional APIauto 用于为枚举成员获取适当的值; 可能会被覆盖

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_ 属性是必需的,因为定义顺序在记录之前丢失。


_私人__姓名

私有名称 将是 Python 3.11 中的普通属性,而不是错误或成员(取决于名称是否以下划线结尾)。 在 3.9 和 3.10 中使用这些名称将发出 DeprecationWarning


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.11 中删除。


在 3.5 版中更改。


Enum 类和成员的布尔值

与非Enum类型(如intstr等)混合的Enum成员根据混合-输入类型的规则; 否则,所有成员评估为 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>

笔记

在 3.11 中,未命名的标志组合只会产生规范标志成员(又名单值标志)。 所以 Color(7) 会产生类似 <Color.BLUE|GREEN|RED: 7> 的东西。