Python 的原生类型中并不包含枚举类型。为了提供更好的解决方案,Python 通过 PEP 435 在 3.4 版本中添加了 enum 标准库。
官网:https://docs.python.org/3.6/library/enum.html
枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期、月份、状态等。在没有专门提供枚举类型的时候我们是怎么做呢,一般就通过字典或类来实现:
Color = {
'RED' : 1,
'GREEN': 2,
'BLUE' : 3,
}
class Color:
RED = 1
GREEN = 2
BLUE = 3
这种来实现枚举如果小心翼翼地使用当然没什么问题,毕竟是一种妥协的解决方案。它的隐患在于可以被修改。更好的方式是使用标准库提供的 Enum 类型,官方库值得信赖。3.4 之前的版本也可以通过 pip install enum 下载支持的库。
enum模块
enum模块定义了:
- 4种枚举类:Enum, IntEnum, Flag, IntFlag
- 装饰器:unique()
- 助手:auto
Flag, IntFlag, auto在python3.6中加入的。
创建枚举
方式一:实例化枚举基类。
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
print(Month.Apr.value)
for name, member in Month.__members__.items():
print(name, '=>', member, ',', member.value)
from enum import Enum
colour = Enum('colour',{'red':6, 'green':3, 'blue':5})
print(colour.red.value)
for name,member in colour.__members__.items():
print(name,'=>', member, ',', member.value)
完整的API如下:Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)
方式二:自定义枚举类。自定义枚举类必须继承自一个枚举基类,至多一个具体的数据类型以及0至多个混合类,自定义的枚举类不能实例化。
from enum import Enum
class Color(Enum):
RED = 2
GREEN = 3
BLUE = 6
print(Color.RED.value)
# c = Color() #不能实例化,否则会报错
定义枚举时,成员名称不允许重复
from enum import Enum
class Color(Enum):
red = 1
red = 2
上面的代码,就无法执行。提示错误:TypeError: Attempted to reuse key: 'red'
默认情况下,不同的成员值允许相同。但是两个相同值的成员,第二个成员的名称被视作第一个成员的别名
from enum import Enum
class Color(Enum):
red = 1
red_alias = 1
成员Color.red和Color.red_alias具有相同的值,那么成员Color.red_alias的名称red_alias就被视作成员Color.red名称red的别名。
如果枚举中存在相同值的成员,在通过值获取枚举成员时,只能获取到第一个成员
from enum import Enum
class Color(Enum):
red = 1
red_alias = 1
print(Color(1))
输出结果为:Color.red
如果要限制定义枚举时,不能定义相同值的成员。可以使用装饰器@unique【要导入unique模块】
from enum import Enum, unique
@unique
class Color(Enum):
red = 1
red_alias = 1
注意点:
1. 枚举值可以是任何类型,如果值不重要可以使用auto()自动选择。但在有其他已定义的值的情况下,谨慎与auto混用
2. Color是枚举类,Color.RED等是枚举成员,枚举成员拥有name和value属性
3. 虽然使用class关键字创建,但枚举并不是常规意义上的python类
对枚举成员及其属性的程序化访问
from enum import Enum
class Color(Enum):
RED = 2
GREEN = 3
BLUE = 6
#查看所有枚举成员
print(list(Color))
for i in Color: #枚举支持按照定义时的顺序进行迭代。
print(i)
#枚举成员的展现形式,有name属性和value属性
print(Color.BLUE,end='\n'*2)
#查看枚举成员的名称
print(Color.RED.name,end='\n'*2)
#查看枚举成员的值
print(Color.RED.value,end='\n'*2)
#枚举成员的type类型是其所属的枚举类
print(type(Color.BLUE))
print(isinstance(Color.RED, Color),end='\n'*2)
#枚举成员是可哈希的,因此可以在字典和集合中使用
apples = {}
apples[Color.RED] = 'red delicious'
apples[Color.GREEN] = 'granny smith'
print(apples,end='\n'*2)
通过成员的名称来获取成员
Color['red']
通过成员值来获取成员
Color(2)
通过成员,来获取它的名称和值
red_member = Color.red
red_member.name
red_member.value
自动生成枚举值
对于不重要的枚举值,可以使用auto自动生成。
from enum import Enum, auto
class Color(Enum):
RED = auto()
BLUE = auto()
GREEN = auto()
auto生成什么值取决于_generate_next_value_()方法,可重写。
from enum import Enum,auto
class AutoName(Enum):
def _generate_next_value_(name, start, count, last_values):
return name
class Ordinal(AutoName):
NORTH = auto()
SOUTH = auto()
EAST = auto()
迭代
枚举支持迭代器,可以遍历枚举成员,如果枚举有值重复的成员,循环遍历枚举时只获取值重复成员的第一个成员。
from enum import Enum
class Color(Enum):
red = 1
orange = 2
yellow = 3
green = 4
blue = 5
indigo = 6
purple = 7
red_alias = 1
for color in Color:
print(color)
如果想把值重复的成员也遍历出来,要用枚举的一个特殊属性__members__,__members__属性是一个映射了枚举成员及其名称的有序字典,包括成员别名。
from enum import Enum
class Color(Enum):
red = 1
orange = 2
yellow = 3
green = 4
blue = 5
indigo = 6
purple = 7
red_alias = 1
for color in Color.__members__.items():
print(color)
枚举比较
枚举的成员可以通过 is 同一性比较或通过 == 等值比较。不能进行大小比较。
from enum import Enum
class Color(Enum):
RED = 2
GREEN = 3
BLUE = 6
red = 2
#身份比较
print(Color.RED is Color.RED)
print(Color.RED is Color.red)
print(Color.RED is Color.BLUE)
print(Color.RED is not Color.BLUE)
#等值比较
print(Color.RED == Color.red)
print(Color.BLUE != Color.red)
#不能进行大小比较,否则报错。
# print(Color.BLUE >= Color.red)
扩展枚举 IntEnum
IntEnum 是 Enum 的扩展,可以枚举成员可以进行比较(实质是枚举成员的值进行比较),不同类型的整数枚举也可以相互比较。
from enum import IntEnum
class Shape(IntEnum):
red = 1
green = 2
blue = 6
class Request(IntEnum):
RED = 1
GREEN = 2
BLUE = 9
print(Shape.red == 1) # True
print(Shape.green < 3) # True
print(Shape.blue <= Shape.red) #False
print(Shape.red == Request.RED) # True
print(Shape.blue >= Request.BLUE) # False