枚举 enum

2021-02-26  本文已影响0人  butters001

以下内容基本来自python官方文档,有兴趣的同学可以去查看原文档

枚举表示的是常量,因此,建议枚举成员名称使用大写字母。
本模块定义了四个枚举类,用来定义名称与值的唯一组合: EnumIntEnumFlagIntFlag。此外,还定义了一个装饰器,unique(), 和一个辅助类,auto

1. 定义一个基础的枚举类

枚举是由 class句法创建的,这种方式易读、易写。
from enum import Enum

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

注解:成员值(如上例中的1,2,3)可以是 intstr 等。若无需设定确切值,auto 实例可以自动为成员分配合适的值。将 auto 与其他值混用时必须要慎重。

注解:虽然 Enum 由 class 语法创建,但 Enum 并不是常规的 Python 类。

注解:

枚举成员的字符串表现形式更容易理解
In [16]: print(Color.RED)
Color.RED
同时,它的 repr 包含更多信息:
In [18]: print(repr(Color.RED))
<Color.RED: 1>
枚举成员的 类型 就是它所属的枚举:
In [19]: type(Color.RED)
Out[19]: <enum 'Color'>

In [20]: isinstance(Color.GREEN, Color)
Out[20]: True
Enum 成员还包含 name 和 value 属性:
In [21]: Color.RED.name
Out[21]: 'RED'

In [22]: Color.RED.value
Out[22]: 1
枚举按定义的顺序进行迭代:

这里可以看出,枚举类和普通的python类是不一样的,遍历这点就不同。

In [23]: for color in Color:
    ...:     print(color)
    ...: 
Color.RED
Color.GREEN
Color.BLUE
枚举成员可哈希,可用于字典和集合:
In [24]: apples = {}

In [25]: apples[Color.RED] = 'red delicious'

In [26]: apples[Color.GREEN] = 'granny smith'

In [27]: apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
Out[27]: True

2. 枚举成员及其属性的访问

有时,要在程序中访问枚举成员(如,开发时不知道颜色的确切值,Color.RED 不适用的情况)。Enum 支持如下访问方式:
In [28]: Color(1)
Out[28]: <Color.RED: 1>

In [29]: Color(3)
Out[29]: <Color.BLUE: 3>
用 name 访问枚举成员时,可使用项目名称:
In [30]: Color['RED']
Out[30]: <Color.RED: 1>

In [31]: Color['GREEN']
Out[31]: <Color.GREEN: 2>
可访问枚举成员的 name 或 value:
In [32]: member = Color.RED

In [33]: member.name
Out[33]: 'RED'

In [34]: member.value
Out[34]: 1

3. 重复的枚举成员和值

两个枚举成员的名称不能相同:

In [35]: class Shape(Enum):
    ...:     SQUARE = 2
    ...:     SQUARE = 3
    ...: 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
...
TypeError: Attempted to reuse key: 'SQUARE'

但是,两个枚举成员可以有相同的值。假设,成员 A 和 B 的值相同(先定义的是 A),则 B 是 A 的别名。按值查找 A 和 B 的值返回的是 A。按名称查找 B,返回的也是 A:

In [36]: class Shape(Enum):
    ...:     SQUARE = 2
    ...:     DIAMOND = 1
    ...:     CIRCLE = 3
    ...:     ALIAS_FOR_SQUARE = 2
    ...: 

In [37]: Shape.SQUARE
Out[37]: <Shape.SQUARE: 2>

In [38]: Shape.ALIAS_FOR_SQUARE
Out[38]: <Shape.SQUARE: 2>

In [39]: Shape(2)
Out[39]: <Shape.SQUARE: 2>

4. 确保唯一枚举值

默认情况下,枚举允许多个名称作为一个值的别名。如需禁用此行为,下述装饰器可以确保枚举中的值仅能只用一次:
@enum.unique

In [40]: from enum import Enum, unique
In [41]: @unique
    ...: class Mistake(Enum):
    ...:     ONE = 1
    ...:     TWO = 1
    ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
...
ValueError: duplicate values found in <enum 'Mistake'>: TWO -> ONE

5. 使用自动设定的值

如果确切的值不重要,你可以使用 auto:

In [42]: from enum import Enum, auto

In [43]: class Color(Enum):
    ...:     RED = auto()
    ...:     BLUE = auto()
    ...:     GREEN = auto()
    ...: 

In [44]: list(Color)
Out[44]: [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

值将由 generate_next_value() 来选择,该函数可以被重载:

In [45]: 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()
    ...: 

In [46]: list(Ordinal)
Out[46]: 
[<Ordinal.NORTH: 'NORTH'>,
 <Ordinal.SOUTH: 'SOUTH'>,
 <Ordinal.EAST: 'EAST'>,
 <Ordinal.WEST: 'WEST'>]

Enum中有 _generate_next_value_ 方法,这里被覆盖。

6. 迭代

对枚举成员的迭代不会迭代出别名部分:

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

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

In [49]: for name, member in Shape.__members__.items():
    ...:     print(name, member)
    ...: 
    ...: 
SQUARE Shape.SQUARE
DIAMOND Shape.DIAMOND
CIRCLE Shape.CIRCLE
ALIAS_FOR_SQUARE Shape.SQUARE

__members__ 属性可被用于对枚举成员进行详细的程序化访问。 例如,找出所有别名:

In [50]: [name for name, member in Shape.__members__.items() if member.name != name]
Out[50]: ['ALIAS_FOR_SQUARE']

7. 比较

枚举成员是按标识号进行比较的:

In [51]: Shape.SQUARE is Shape.SQUARE
Out[51]: True

In [52]: Shape.SQUARE is Shape.CIRCLE
Out[52]: False

# 和别名相等
In [53]: Shape.SQUARE is Shape.ALIAS_FOR_SQUARE
Out[53]: True

枚举值之间的排序比较 不被 支持。 Enum 成员不属于整数。即不能进行 ><比较。

In [55]: Shape.SQUARE > Shape.CIRCLE
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-55-811bd6fff84e> in <module>
----> 1 Shape.SQUARE > Shape.CIRCLE

TypeError: '>' not supported between instances of 'Shape' and 'Shape'

相等比较的定义如下:

In [56]: Shape.SQUARE == Shape.CIRCLE
Out[56]: False

In [57]: Shape.SQUARE != Shape.CIRCLE
Out[57]: True

In [58]: Shape.SQUARE == Shape.ALIAS_FOR_SQUARE
Out[58]: True

与非枚举值的比较将总是不相等

In [59]: Shape.SQUARE == 2
Out[59]: False

8. 允许的枚举成员和属性

以上示例使用整数作为枚举值。 如果值 确实 重要,则枚举可以使用任意的值。
枚举属于 Python 的类,并可具有普通方法和特殊方法。 如果我们有这样一个枚举:

class Mood(Enum):
    FUNKY = 1
    HAPPY = 3

    def describe(self):
        # self is the member here
        # 在这里self 表示枚举的成员
        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
        # 在这里cls表示枚举对象
        return cls.HAPPY

那么:

In [61]: Mood.favorite_mood()
Out[61]: <Mood.HAPPY: 3>

In [62]: Mood.HAPPY.describe()
Out[62]: ('HAPPY', 3)

In [63]: str(Mood.FUNKY)
Out[63]: 'my custom str! 1'

9. 封存

枚举可以被封存与解封:

In [65]: from pickle import dumps, loads

In [66]: Color is loads(dumps(Color))
Out[66]: True

10. 功能性 API

Enum 类属于可调用对象,它提供了以下功能性 API:

In [67]: Animal = Enum('Animal', 'ANT BEE CAT DOG')

In [68]: Animal
Out[68]: <enum 'Animal'>

In [69]: Animal.ANT
Out[69]: <Animal.ANT: 1>

In [70]: Animal.ANT.value
Out[70]: 1

In [71]: list(Animal)
Out[71]: [<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

第一个参数是枚举的名称。
第二个参数是枚举成员名称的 来源。 它可以是一个用空格分隔的名称字符串、名称序列、键/值对 2 元组的序列,或者名称到值的映射(例如字典)。 最后两种选项使得可以为枚举任意赋值;其他选项会自动以从 1 开始递增的整数赋值(使用 start 形参可指定不同的起始值)。

11. 派生的枚举

IntEnum

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

In [72]: from enum import IntEnum
    ...: class Shape(IntEnum):
    ...:     CIRCLE = 1
    ...:     SQUARE = 2
    ...: 
    ...: class Request(IntEnum):
    ...:     POST = 1
    ...:     GET = 2
    ...: 

In [73]: Shape == 1
Out[73]: False

In [74]: Shape.CIRCLE == 1
Out[74]: True

In [75]: Shape.CIRCLE == Request.POST
Out[75]: True

不过,它们仍然不可与标准 Enum 枚举进行比较:

In [76]: class Shape(IntEnum):
    ...:     CIRCLE = 1
    ...:     SQUARE = 2
    ...: 
    ...: class Color(Enum):
    ...:     RED = 1
    ...:     GREEN = 2
    ...: 

In [77]: Shape.CIRCLE == Color.RED
Out[77]: False

IntEnum 值在其他方面的行为都如你预期的一样类似于整数:

In [78]: int(Shape.CIRCLE)
Out[78]: 1

In [79]: ['a', 'b', 'c'][Shape.CIRCLE]
Out[79]: 'b'

In [80]: [i for i in range(Shape.SQUARE)]
Out[80]: [0, 1]
IntFlag

未完待续。。。

上一篇下一篇

猜你喜欢

热点阅读