跳转至
阅读量:

Python 学习之路——枚举类

前言:

首先大家要明白的是:枚举类不是Python的原生类!不是原生类!不是!Python中的枚举类是依靠 enum模块 来实现的。 枚举是在 Python3.4 添加的新功能(安装Python解释器后自带的官方模块),3.4以前的版本需要使用pip install enum来安装模块后使用。

一、枚举

什么是枚举?

枚举可看作是一系列符号名称的集合,集合中每一个元素要保证唯一性和不可变(与字典类似)。因此我们可以对枚举中元素进行恒等比较,通俗来讲枚举就是一系列常量的集合,枚举是可迭代的。

枚举的作用

首先我们先思考一下,在 Python中我们如何定义常量?

我们最常用的做法是采用变量名大写的方式来定义常量。

这种方式虽然简单,但问题在于我们定义的仍然是变量、是可以被修改的,而常量是什么呢?简单来说就是不可变的量。

枚举就有不可变的特性,因此枚举的主要作用就来定义常量

二、代码实现

enum 库中定义了四个枚举类,它们是可被用来定义名称和值的不重复集合: Enum, IntEnum, Flag 和 IntFlag。 此外还定义了一个装饰器 unique() 和一个辅助类 auto。 | 名称 | 功能 | | -------- | ------------------------------------------------------------ | | Enum | 创建枚举常量 | | IntEnum | 创建属于 int 的子类的枚举常量 | | IntFlag | 创建可使用按位运算符进行组合而不会丢失其 IntFlag 成员资格的枚举常量。 IntFlag 成员同样也是 int 的子类 | | Flag | 创建可使用按位运算符进行组合而不会丢失其 Flag 成员资格的枚举常量 | | unique() | 确保只将一个名称绑定到任意一个值 | | auto | 实例会被替换为一个可作为 Enum 成员的适当的值。 初始值从 1 开始 |

1. Enum类

继承Enum类进行创建:

from enum import Enum

class Color(Enum):
    BLACK = '#000000'
    RED = '#FF0000'
    GREEN = '#00FF00'
    BLUE = '#0000FF'
    WHITE = '#FFFFFF'


print(Color)    # <enum 'Color'>
print(Color.RED)    # Color.RED
print(Color.BLACK.name)    # BLACK
print(Color.GREEN.value)    # #00FF00

枚举类中不允许有同名的枚举成员,当定义两个同名枚举成员时就会抛出错误:

class Animal(Enum):
    CAT = 1
    CAT = 2

Animal()
# TypeError: Attempted to reuse key: 'CAT'

直接使用Enum进行创建:

from enum import Enum

Animal = Enum('Animal', 'ANT BEE CAT DOG')
print(Animal)    # <enum 'Animal'>
print(Animal.ANT)    # <Animal.ANT>
print(Animal.ANT.value)   # 1
Enum 传入的第一参数是枚举的名称,第二个参数是枚举成员名称的 来源。 它可以是一个用空格分隔的名称字符串、名称序列、键/值对 2 元组的序列,或者名称到值的映射(例如字典)。

最后两种选项使得可以为枚举任意赋值;其他选项会自动以从 1 开始递增的整数赋值(使用 start 形参可指定不同的起始值)。

枚举类可以直接使用 for语句进行遍历:

from enum import Enum

Animal = Enum('Animal', 'ANT BEE CAT DOG')
for i in Animal:
    print(i, '*', i.name, '*', i.value)
'''
Animal.ANT * ANT * 1
Animal.BEE * BEE * 2
Animal.CAT * CAT * 3
Animal.DOG * DOG * 4
'''

枚举成员之间可以使用 is 和 ==、!=进行比较,但不能使用< 、>、>=、<=进行比较。

from enum import Enum

class Animal(Enum):
    CAT = 1
    DOG = 0


print(Animal.CAT == Animal.DOG)    # False
print(Animal.CAT is Animal.DOG)    # False

2. IntEnum类

IntEmum 的用法与 Enum 类似,但是 IntEnum 的成员可与整数进行比较:

from enum import IntEnum

class Shape(IntEnum):
    CIRCLE = 1
    SQUARE = 2

class Request(IntEnum):
    POST = 1
    GET = 2

print(Shape == 1)    # False
print(Shape.CIRCLE == 1)    # True
print(Shape.CIRCLE == Request.POST)    # True

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

class Shape(IntEnum):
    CIRCLE = 1
    SQUARE = 2

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

print(Shape.CIRCLE == Color.RED)    # False

3. IntFlag类

IntFlag 类同样是基于 int 的,不同之处在于 IntFlag 成员可使用按位运算符 &, |, ^, ~ 进行组合且结果仍然为 IntFlag 成员。 正如名称所表明的,IntFlag 成员同时也是 int 的子类,并能在任何使用 int 的场合被使用。 IntFlag 成员进行除按位运算以外的其他运算都将导致失去 IntFlag 成员资格。

from enum import IntFlag

class Perm(IntFlag):
    R = 4
    W = 2
    X = 1

print(Perm.R | Perm.W)    # Perm.R|W
print(Perm.R + Perm.W)    # 6
RW = Perm.R | Perm.W
print(Perm.R in RW)    # True
print(Perm.R | 8)    # Perm.8|R

4. Flag类

与 IntFlag 类似,Flag 成员可使用按位运算符 (&, |, ^, ~) 进行组合,与 IntFlag 不同的是它们不可与任何其它 Flag 枚举或 int 进行组合或比较。 虽然可以直接指定值,但推荐使用 auto 作为值以便让 Flag 选择适当的值。

from enum import Flag, auto

class Color(Flag):
    RED = auto()
    BLUE = auto()
    GREEN = auto()

print(Color.RED & Color.GREEN)    # Color.0
print(bool(Color.RED & Color.GREEN))    # False

5. unique装饰器

默认情况下,枚举允许有多个名称作为某个相同值的别名。 如果不想要这样的行为,可以使用 unique 装饰器来确保每个值在枚举中只被使用一次:

from enum import Enum, unique

@unique
class Mistake(Enum):
    ONE = 1
    TWO = 2
    THREE = 3
    FOUR = 3
# ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

6. auto函数

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

from enum import Enum, auto
class Color(Enum):
    RED = auto()
    BLUE = auto()
    GREEN = auto()

print(list(Color))
# [<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

三、官方文档

有关 Enum模块 的更多信息可以参考官方文档:官方文档

评论