什么是enum,为什么需要它?
枚举类型,也称为enum,是一种数据类型,由一组命名值组成。这些可枚举的命名值充当计算机语言中的常量。例如,COLOR枚举可以包括诸如RED, GREEN, BLUE等命名值。这些命名值都是大写,以区别于变量。
为什么要使用枚举呢?
可以想像这样一种情景:需要在网站中将用户的性别限制为MALE, FEMALE和N/A。当然,字符串列表也可以胜任,如user.gender='MALE',user.gender='FEMALE'。但使用字符串作为性别属性值显得不够强壮,程序员在使用过程中容易出错或者受到恶意攻击。程序员很容易将'MALE'误拼写为'ALE',或者将'FEMALE'误拼写为'EMAIL',代码虽然仍可以运行,但会产生严重结果。攻击者可能会仔细构建垃圾字符串值,导致系统崩溃或获取根访问权限。如果使用枚举,则可将user.gender属性的值限制在有限的值列表中,这样前面提到的问题就不会出现了。
Python中使用枚举的技巧与建议
enum有助于为属性限制可选的值,这在实际编程中非常有用。在处理数据库时,enum在Python和数据库管理系统中应用是等同的,这样就可以避免很多难以发现的bug。
使用enum可以有效地保护系统,使其杜绝恶意输入。在系统接受用户输入前,一定要检查输入的值。
Python中的枚举数据类型
枚举的字面含义是指列出有穷集合中的所有元素,即一一列举的意思。在Python中,枚举可以视为是一种数据类型,当一个变量的取值只有几种有限的情况时,我们可以将其声明为枚举类型。例如表示周几的这一变量weekday,只有七种可能的取值,我们就可以将其声明为枚举类型。
枚举的类型该如何实现?
我们一个很直观的想法是:可以通过类的方式来实现,变量就是类,变量所有可能的取值作为类的变量。之后访问的时候,通过类名+变量名的方式就可以进行访问:
class Weekday():
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
print(Weekday.wednesday) # 3
但是这样实现枚举类型的方式存在一定的问题:
1、枚举的枚举项不能出现重复,即key不应该相同(以上例来讲,不能出现两个wednesday)
2、枚举项的值不应该允许在外部进行修改(枚举类型一般是固定下来的常量,声明好之后不能在外部随意更改)
如下所示:
class Weekday():
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
wednesday = 333
print(Weekday.wednesday) # 333
Weekday.wednesday = "星期三"
print(Weekday.wednesday) # 星期三
为了解决上述两个问题,我们可以使用Python中提供的enum模块。
enum的使用
enum模块是系统内置模块,可以直接使用import导入,但是在导入的时候,不建议使用import enum将enum模块中的所有数据都导入,一般使用的最多的就是enum模块中的Enum、IntEnum、unique这几项。
借助enum模块,我们在实现上述需求的时候,就可以这样来做:
from enum import Enum
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
print(Weekday.wednesday) # Weekday.wednesday
print(type(Weekday.wednesday)) # <enum 'Weekday'>
print(Weekday.wednesday.name) # wednesday
print(Weekday.wednesday.value) # 3
我们自定义的枚举类型继承Enum基类,之后还是通过Weekday.wednesday获取得到的是枚举成员,通过.name和.value可以获得枚举成员对应的属性。
这时,如果我们在Weekday类中,声明重复的枚举成员,会产生错误:
TypeError: Attempted to reuse key: 'wednesday'
如果在外部进行枚举成员属性值的修改,也会产生错误:
AttributeError: Cannot reassign members.
这样的话,就解决之前存在的两个问题。
此外,对于枚举成员,.name和.value是其内置属性,我们还可以给枚举成员增加其他属性。实现的方式如下:
from enum import Enum
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
Weekday.wednesday.label = "星期三"
Weekday.wednesday.work = "完成假期作业"
Weekday.wednesday.time = 10
print(Weekday.wednesday.label) # 星期三
print(Weekday.wednesday.work) # 完成假期作业
print(Weekday.wednesday.time) # 10
在获取枚举类型的成员时,我们可以通过类名+key的方式实现,即上面的Weekday.wednesday。此外,通过key获取枚举成员的时候,还可以使用Weekday['key']这样的方式。
也可以通过value来获取枚举成员,通过value获取枚举成员是:Weekday(value)这样的方式。
如下所示:
from enum import Enum
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
Weekday.wednesday.label = "星期三"
Weekday.wednesday.work = "完成假期作业"
Weekday.wednesday.time = 10
obj_1 = Weekday.wednesday
print(obj_1.label) # 星期三
obj_2 = Weekday['wednesday']
print(obj_1.label) # 星期三
obj_3 = Weekday(3)
print(obj_3.label) # 星期三
enum的总结
1、枚举类不能用来实例化对象
例如下面的用法是错误的:
from enum import Enum
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
w = Weekday()
print(w.wednesday)
2、访问枚举类中的某个枚举成员,可以有三种方式,详见上面使用部分的介绍。
3、枚举类里面定义的 key = value,在类外部不能修改value值,详见上面使用部分的介绍。
4、枚举成员可以用来比较,使用==或者is。
from enum import Enum
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
obj_1 = Weekday.wednesday
obj_2 = Weekday['wednesday']
obj_3 = Weekday(3)
print(obj_1==obj_2==obj_3) # True
print(obj_1 is obj_2 is obj_3) # True
5、继承Enum基类之后,一个枚举类中的key和value,key不能相同,但是value可以相同。
from enum import Enum
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
test = 3
print(Weekday.test.value) # 3
如果想让value也不相同的话,可以导入unique。如下所示:
from enum import Enum, unique
@unique
class Weekday(Enum):
monday = 1
tuesday = 2
wednesday = 3
thirsday = 4
friday = 5
saturday = 6
sunday = 7
test = 3
print(Weekday.test.value) # 3
此时的代码会报如下错误:
6、如果要枚举类中的Value只能是整型数字,那么,可以导入IntEnum,然后继承IntEnum即可。注意,此时,如果value为字符串的数字,也不会报错。