8.13. enum — 支持枚举 — Python 文档
8.13. 枚举 — 支持枚举
3.4 版中的新功能。
枚举是一组绑定到唯一常量值的符号名称(成员)。 在枚举中,成员可以通过身份进行比较,并且可以迭代枚举本身。
8.13.1. 模块内容
该模块定义了四个可用于定义唯一名称和值集的枚举类:Enum、IntEnum、Flag和IntFlag ]。 它还定义了一个装饰器 unique() 和一个帮助器 auto。
- class enum.Enum
- 用于创建枚举常量的基类。 请参阅 Functional API 部分以获取替代构造语法。
- class enum.IntEnum
- 用于创建枚举常量的基类,这些常量也是 int 的子类。
- class enum.Flag
- 用于创建枚举常量的基类,可以使用按位运算进行组合,而不会丢失其 Flag 成员资格。
- enum.unique()
- 枚举类装饰器,确保只有一个名称绑定到任何一个值。
- class enum.auto
- 实例被替换为 Enum 成员的适当值。
3.6 版新增:Flag
、IntFlag
、auto
8.13.2. 创建一个枚举
枚举是使用 class 语法创建的,这使得它们易于阅读和编写。 Functional API 中描述了另一种创建方法。 要定义枚举,子类 Enum 如下:
笔记
命名法
- 类
Color
是一个 枚举 (或 枚举 ) - 属性
Color.RED
、Color.GREEN
等是枚举成员(或枚举成员),是函数常量。 - 枚举成员有 names 和 values(
Color.RED
的名称是RED
,Color.BLUE
的值是3
等)
枚举成员具有人类可读的字符串表示形式:
……而他们的 repr
有更多信息:
枚举成员的 type 是它所属的枚举:
Enum 成员还有一个属性,只包含它们的项目名称:
枚举支持迭代,按定义顺序:
枚举成员是可散列的,因此它们可以在字典和集合中使用:
8.13.3. 对枚举成员及其属性的编程访问
有时以编程方式访问枚举中的成员很有用(即 Color.RED
无法执行的情况,因为在编写程序时不知道确切的颜色)。 Enum
允许这样的访问:
如果要通过 name 访问枚举成员,请使用项目访问:
如果您有枚举成员并需要其 name
或 value
:
8.13.4. 复制枚举成员和值
有两个同名的枚举成员是无效的:
但是,允许两个枚举成员具有相同的值。 给定两个具有相同值的成员 A 和 B(首先定义 A),B 是 A 的别名。 按值查找 A 和 B 的值将返回 A。 B 的按名称查找也将返回 A:
笔记
不允许尝试创建与已定义属性(另一个成员、方法等)同名的成员或尝试创建与成员同名的属性。
8.13.5. 确保唯一的枚举值
默认情况下,枚举允许多个名称作为同一个值的别名。 当不需要这种行为时,可以使用以下装饰器来确保每个值在枚举中只使用一次:
- @enum.unique
专门用于枚举的 class 装饰器。 它搜索枚举的 __members__
收集它找到的任何别名; 如果发现任何 ValueError 会引发详细信息:
8.13.7. 迭代
迭代枚举的成员不提供别名:
特殊属性 __members__
是将名称映射到成员的有序字典。 它包括枚举中定义的所有名称,包括别名:
__members__
属性可用于对枚举成员进行详细的编程访问。 例如,查找所有别名:
8.13.8. 比较
枚举成员按身份进行比较:
枚举值之间的有序比较不受 支持 。 枚举成员不是整数(但请参阅下面的 IntEnum):
虽然定义了相等比较:
与非枚举值的比较总是不相等(同样,IntEnum 被明确设计为行为不同,见下文):
8.13.9. 允许的枚举成员和属性
上面的示例使用整数作为枚举值。 使用整数既简短又方便(默认情况下由 Functional API 提供),但并未严格执行。 在绝大多数用例中,人们并不关心枚举的实际值是什么。 但是,如果值 是 重要,则枚举可以具有任意值。
枚举是 Python 类,可以像往常一样具有方法和特殊方法。 如果我们有这个枚举:
然后:
允许的规则如下:以单个下划线开头和结尾的名称由 enum 保留,不能使用; 枚举中定义的所有其他属性都将成为该枚举的成员,但特殊方法(__str__()
、__add__()
等)和描述符(方法也是描述符)除外。
注意:如果您的枚举定义了 __new__()
和/或 __init__()
,那么赋予枚举成员的任何值都将传递到这些方法中。 有关示例,请参见 行星 。
8.13.10。 枚举的受限子类化
仅当枚举未定义任何成员时,才允许对枚举进行子类化。 所以这是被禁止的:
但这是允许的:
允许定义成员的枚举的子类化将导致违反类型和实例的一些重要不变量。 另一方面,允许在一组枚举之间共享一些共同的行为是有意义的。 (有关示例,请参阅 OrderedEnum。)
8.13.11。 酸洗
枚举可以被pickled和unpickled:
酸洗的通常限制适用:酸洗枚举必须在模块的顶层定义,因为取消酸洗要求它们可以从该模块导入。
笔记
使用 pickle 协议版本 4,可以轻松地pickle 嵌套在其他类中的枚举。
可以通过在枚举类中定义 __reduce_ex__()
来修改 Enum 成员的酸洗/取消酸洗方式。
8.13.12。 功能API
Enum 类是可调用的,提供以下功能 API:
这个 API 的语义类似于 namedtuple。 调用 Enum 的第一个参数是枚举的名称。
第二个参数是枚举成员名称的 source。 它可以是用空格分隔的名称字符串、名称序列、具有键/值对的 2 元组序列或映射(例如 字典)的名称到值。 最后两个选项允许为枚举分配任意值; 其他自动分配从 1 开始的递增整数(使用 start
参数指定不同的起始值)。 返回一个从 Enum 派生的新类。 换句话说,上面对 Animal
的赋值等价于:
默认为 1
作为起始编号而不是 0
的原因是 0
在布尔意义上是 False
,但枚举成员都评估为 [ X164X]。
使用函数式 API 创建的酸洗枚举可能会很棘手,因为帧堆栈实现细节用于尝试找出枚举是在哪个模块中创建的(例如 如果您在单独的模块中使用实用程序函数,它将失败,并且也可能无法在 IronPython 或 Jython 上运行)。 解决方案是明确指定模块名称,如下所示:
警告
如果没有提供 module
,并且 Enum 无法确定它是什么,则新的 Enum 成员将不会是 unpicklable; 为了使错误更接近源头,将禁用酸洗。
在某些情况下,新的 pickle 协议 4 还依赖于 __qualname__ 被设置为 pickle 能够找到类的位置。 例如,如果该类在全局范围内的 SomeData 类中可用:
完整的签名是:
- 价值
新的 Enum 类将作为其名称记录的内容。
- 名字
枚举成员。 这可以是空格或逗号分隔的字符串(除非另有说明,否则值将从 1 开始):
或名称迭代器:
或(名称,值)对的迭代器:
或映射:
- 模块
可以在其中找到新 Enum 类的模块的名称。
- 名称
在模块中可以找到新的 Enum 类。
- 类型
键入以混合到新的 Enum 类中。
- 开始
如果仅传入名称,则开始计数的数字。
3.5 版更改: 添加了 start 参数。
8.13.13。 派生枚举
8.13.13.1。 枚举
提供的 Enum 的第一个变体也是 int 的子类。 IntEnum 的成员可以比作整数; 通过扩展,不同类型的整数枚举也可以相互比较:
但是,它们仍然无法与标准的 Enum 枚举进行比较:
IntEnum 值在您期望的其他方面表现得像整数:
8.13.13.2. 内部标志
Enum 的下一个变体 IntFlag 也是基于 int。 不同之处在于内部标志可以使用按位运算符(&、|、^、~)组合成员,结果仍然是内部标志成员。 然而,顾名思义,IntFlag 成员也是 int 的子类,并且可以在使用 int 的任何地方使用。 除了按位操作之外,对 IntFlag 成员的任何操作都将失去 IntFlag 成员资格。
3.6 版中的新功能。
示例 IntFlag 类:
也可以命名组合:
IntFlag 和 Enum 之间的另一个重要区别是,如果没有设置标志(值为 0),则其布尔值计算为 False:
因为 IntFlag 成员也是 int 的子类,它们可以与它们组合:
8.13.13.3。 旗帜
最后一个变体是 Flag。 喜欢内部标志 , 旗帜可以使用按位运算符(&、|、^、~)组合成员。 与 IntFlag 不同,它们不能与任何其他 Flag 枚举或 int 组合或比较。 虽然可以直接指定值,但建议使用 auto 作为值并让 Flag 选择合适的值。
3.6 版中的新功能。
与 IntFlag 一样,如果 Flag 成员的组合导致未设置标志,则布尔评估为 False:
单个标志的值应该是 2 的幂(1、2、4、8……),而标志的组合则不会:
为“无标志设置”条件命名不会改变其布尔值:
8.13.13.4。 其他
虽然 IntEnum 是 enum 模块的一部分,但独立实现非常简单:
这演示了如何定义类似的派生枚举; 例如 StrEnum
混合在 str 而不是 int。
一些规则:
- 当子类化 Enum 时,混合类型必须出现在 Enum 本身之前的碱基序列,如上面的 IntEnum 示例。
- 虽然 Enum 可以有任何类型的成员,一旦你混合了一个额外的类型,所有的成员都必须有那个类型的值,例如 int 以上。 此限制不适用于仅添加方法且未指定其他数据类型(例如 int 或 str)的 mix-in。
- 当混入另一种数据类型时,
value
属性与枚举成员本身的 不相同 ,尽管它是等效的并且将比较相等。 - % 样式格式: %s 和 %r 分别调用 Enum 类的
__str__()
和__repr__()
; 其他代码(例如 IntEnum 的 %i 或 %h)将枚举成员视为其混合类型。 - 格式化字符串文字、str.format()和format()将使用混合类型的
__format__()
。 如果需要 Enum 类的 str() 或 repr(),请使用 !s 或 !r ] 格式代码。
8.13.14。 有趣的例子
虽然 Enum、IntEnum、IntFlag 和 Flag 预计将涵盖大多数用例,但它们无法涵盖所有用例。 以下是一些可以直接使用的不同类型枚举的方法,或者作为创建自己的示例的方法。
8.13.14.1。 省略值
在许多用例中,人们并不关心枚举的实际值是多少。 有几种方法可以定义这种类型的简单枚举:
使用这些方法中的任何一种都向用户表明这些值并不重要,并且还使用户能够添加、删除或重新排序成员,而无需对其余成员重新编号。
无论您选择哪种方法,您都应该提供一个 repr() 来隐藏(不重要的)值:
8.13.14.1.3。 使用描述性字符串
使用字符串作为值将如下所示:
8.13.14.1.4。 使用自定义__new__()
使用自动编号 __new__()
看起来像:
笔记
__new__()
方法(如果已定义)在创建 Enum 成员期间使用; 然后它被 Enum 的 __new__()
替换,它在类创建后用于查找现有成员。
8.13.14.3。 重复自由枚举
如果发现重复的成员名称而不是创建别名,则会引发错误:
8.13.14.4。 行星
如果定义了 __new__()
或 __init__()
,枚举成员的值将传递给这些方法:
8.13.15。 枚举有什么不同?
枚举有一个自定义元类,它影响派生的枚举类及其实例(成员)的许多方面。
8.13.15.1。 枚举类
EnumMeta
元类负责提供 __contains__()
、__dir__()
、__iter__()
和其他允许使用 Enum 做事的方法] 在典型类上失败的类,例如 list(Color) 或 some_var in Color。 EnumMeta
负责确保最终的 Enum 类上的各种其他方法是正确的(例如 __new__()
、__getnewargs__()
、__str__()
和 __repr__()
)。
8.13.15.2. 枚举成员(又名实例)
Enum 成员最有趣的地方在于它们是单例的。 EnumMeta
在创建 Enum 类本身的同时创建它们,然后放置一个自定义的 __new__()
以确保没有新的被实例化,只返回现有成员实例。
8.13.15.3。 更细的点
8.13.15.3.1。 支持的__dunder__名字
__members__
是 member_name
:member
项目的 OrderedDict
。 它仅在课堂上可用。
__new__()
,如果指定,必须创建并返回枚举成员; 适当地设置成员的 _value_
也是一个很好的主意。 一旦创建了所有成员,就不再使用它。
8.13.15.3.2。 支持的_sunder_名字
_name_
– 成员名称_value_
– 成员的价值; 可以在__new__
中设置/修改_missing_
– 找不到值时使用的查找函数; 可能会被覆盖_order_
- 在 Python 2/3 代码中使用,以确保成员顺序一致(类属性,在类创建时移除)_generate_next_value_
– Functional API 和 auto 用于为枚举成员获取适当的值; 可能会被覆盖
3.6 版新增:_missing_
、_order_
、_generate_next_value_
为了帮助保持 Python 2 / Python 3 代码同步,可以提供 _order_
属性。 它将根据枚举的实际顺序进行检查,如果两者不匹配,则会引发错误:
笔记
在 Python 2 代码中,_order_
属性是必需的,因为定义顺序在记录之前丢失。
8.13.15.3.3。 Enum会员类型
Enum 成员是它们的 Enum 类的实例,通常作为 EnumClass.member
访问。 在某些情况下,它们也可以作为 EnumClass.member.member
访问,但是您永远不应该这样做,因为该查找可能会失败,或者更糟的是,除了您正在寻找的 Enum 成员(这是为成员使用全大写名称的另一个好理由):
在 3.5 版中更改。