enum
--- 对枚举的支持¶
3.4 新版功能.
源代码: Lib/enum.py
枚举是一组符号名称(枚举成员)的集合,枚举成员应该是唯一的、不可变的。在枚举中,可以对成员进行恒等比较,并且枚举本身是可迭代的。
注解
枚举成员名的大小写
因为枚举是被用来代表常量的所以我们建议枚举成员名称应使用大写形式,并将在我们的示例中使用此种风格。
模块内容¶
此模块定义了四个枚举类,它们可被用来定义名称和值的不重复集合: Enum
, IntEnum
, Flag
和 IntFlag
。 此外还定义了一个装饰器 unique()
和一个辅助类 auto
。
-
class
enum.
Enum
¶ 此基类用于创建枚举常量。 请参阅 Functional API 小节了解另一种替代性的构建语法。
-
enum.
unique
() 确保一个名称只绑定一个值的 Enum 类装饰器。
-
class
enum.
auto
¶ 实例会被替换为一个可作为 Enum 成员的适当的值。 初始值从 1 开始。
3.6 新版功能: Flag
, IntFlag
, auto
创建一个 Enum¶
枚举是使用 class
语法来创建的,这使得它们易于读写。 另一种替代创建方法的描述见 Functional API。 要定义一个枚举,可以对 Enum
进行如下的子类化:
>>> from enum import Enum
>>> class Color(Enum):
... RED = 1
... GREEN = 2
... BLUE = 3
...
注解
命名法
类
Color
是一个 枚举 (或称 enum)属性
Color.RED
,Color.GREEN
等等是 枚举成员 (或称 enum 成员) 并且被用作常量。枚举成员具有 名称 和 值 (例如
Color.RED
的名称为RED
,Color.BLUE
的值为3
等等)
注解
虽然我们使用 class
语法来创建 Enum,但 Enum 并不是普通的 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
>>>
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>
若要用 名称 访问枚举成员时,可使用枚举项:
>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>
若有了枚举成员,需要获取 name
或 value
:
>>> 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_()
方法的定义必须在任何其他成员之前。
迭代¶
对枚举成员的迭代遍历不会列出别名:
>>> 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']
比较运算¶
枚举成员是按 ID 进行比较的:
>>> 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'
合法的规则如下:以单下划线开头和结尾的名称是保留值,不能使用;在枚举中定义的其他所有属性都将成为该枚举的成员,但特殊方法(__str__()
、__add__()
等)、描述符(方法也是描述符)和在 _ignore_
中列出的变量名除外。
注意:如果你的枚举定义了 __new__()
和/或 __init__()
那么指定给枚举成员的任何值都会被传入这些方法。 请参阅示例 Planet。
受限的 Enum 子类化¶
一个新的 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 )
打包(pickle)¶
枚举类型可以被打包和解包:
>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True
打包的常规限制同样适用于枚举类型:必须在模块的最高层级定义,因为解包操作要求可从该模块导入。
注解
用 pickle 协议版本 4 可以轻松地将嵌入其他类中的枚举进行打包。
通过在枚举类中定义 __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
的第一个参数是枚举的名称。
第二个参数是枚举成员名称的 来源。可以是个用空格分隔的名称字符串、名称序列、表示键/值对的二元组的序列,或者名称到值的映射(如字典)。 最后两种可以为枚举赋任意值;其他类型则会自动赋成由 1 开始递增的整数值(利用 start
形参可指定为其他起始值)。返回值是一个派生自 Enum
的新类。换句话说,上述对 Animal
的赋值等价于:
>>> class Animal(Enum):
... ANT = 1
... BEE = 2
... CAT = 3
... DOG = 4
...
默认以 1
而以 0
作为起始数值的原因在于 0
的布尔值为 False
,但所有枚举成员都应被求值为 True
。
对使用功能性 API 创建的枚举执行封存可能会很麻烦,因为要使用帧堆栈的实现细节来尝试并找出枚举是在哪个模块中创建的(例如当你使用了另一个模块中的工具函数就可能失败,在 IronPython 或 Jython 上也可能无效)。 解决办法是显式地指定模块名称,如下所示:
>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)
警告
如果未提供 module
,且 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)
- value
将被新 Enum 类将记录为其名称的数据。
- names
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}
- module
新 Enum 类所在模块的名称。
- qualname
新 Enum 类在模块中的具体位置。
- type
要加入新 Enum 类的类型。
- start
当只传入名称时要使用的起始数值。
在 3.5 版更改: 增加了 start 形参。
派生的枚举¶
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
。与 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
单个旗标的值应当为二的乘方 (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
其他事项¶
虽然 IntEnum
是 enum
模块的一部分,但要独立实现也应该相当容易:
class IntEnum(int, Enum):
pass
这里演示了如何定义类似的派生枚举;例如一个混合了 str
而不是 int
的 StrEnum
。
几条规则:
虽然
Enum
可以拥有任意类型的成员,不过一旦你混合了附加类型,则所有成员必须为相应类型的值,如在上面的例子中即为int
。 此限制不适用于仅添加方法而未指定另一数据类型如int
或str
的混合类。当混合了另一数据类型时,
value
属性会 不同于 枚举成员自身,但它们仍保持等价且比较结果也相等。%-style formatting: %s 和 %r 会分别调用
Enum
类的__str__()
和__repr__()
;其他代码 (例如表示 IntEnum 的 %i 或 %h) 会将枚举成员视为对应的混合类型。格式化字符串字面值,
str.format()
和format()
将使用混合类型的__format__()
除非在子类中重载了__str__()
或__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
有趣的示例¶
虽然 Enum
, IntEnum
, IntFlag
和 Flag
预期可覆盖大多数应用场景,但它们无法覆盖全部。 这里有一些不同类型枚举的方案,它们可以被直接使用,或是作为自行创建的参考示例。
省略值¶
在许多应用场景中人们都不关心枚举的实际值是什么。 有几个方式可以定义此种类型的简单枚举:
使用以上任何一种方法均可向用户指明值并不重要,并且使人能够添加、移除或重排序成员而不必改变其余成员的数值。
无论你选择何种方法,你都应当提供一个 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: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'
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)>]
各种枚举有何区别?¶
枚举具有自定义的元类,它会影响所派生枚举类及其实例(成员)的各个方面。
枚举类¶
EnumMeta
元类负责提供 __contains__()
, __dir__()
, __iter__()
及其他方法以允许用户通过 Enum
类来完成一般类做不到的事情,例如 list(Color) 或 some_enum_var in Color。 EnumMeta
会负责确保最终 Enum
类中的各种其他方法是正确的 (例如 __new__()
, __getnewargs__()
, __str__()
和 __repr__()
)。
枚举成员(即实例)¶
有关枚举成员最有趣的特点是它们都是单例对象。 EnumMeta
会在创建 Enum
类本身时将它们全部创建完成,然后准备好一个自定义的 __new__()
,通过只返回现有的成员实例来确保不会再实例化新的对象。
细节要点¶
支持的 __dunder__
名称¶
__members__
是一个 member_name
:member
条目的只读有序映射。 它只在类上可用。
如果指定了 __new__()
,它必须创建并返回枚举成员;相应地设定成员的 _value_
也是一个很好的主意。 一旦所有成员都创建完成它就不会再被使用。
支持的 _sunder_
名称¶
_name_
-- 成员的名称_value_
-- 成员的值;可以在__new__
中设置 / 修改_missing_
-- 当未发现某个值时所使用的查找函数;可被重写_order_
-- 用于 Python 2/3 代码以确保成员顺序一致(类属性,在类创建期间会被移除)_generate_next_value_
-- Functional 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
类型(例如 int
, str
等)的 Enum
成员会按所混合类型的规则被求值;在其他情况下,所有成员都将被求值为 True
。 要使你的自定义 Enum 的布尔值取决于成员的值,请在你的类中添加以下代码:
def __bool__(self):
return bool(self.value)
带有方法的 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>