4.0 KiB
如何在 Python 中实现“枚举”
原文:https://www.pythoncentral.io/how-to-implement-an-enum-in-python/
什么是 enum,我们为什么需要它?
枚举类型(也称为枚举)是一种数据类型,由一组命名值组成,这些命名值称为该类型的元素、成员或枚举器。这些枚举的命名值在计算语言中充当常数。例如,COLOR枚举可以包括命名值,如RED、GREEN和BLUE。请注意,所有命名的值都以大写形式书写,以区别它们是常量,这与变量有很大的不同。
那么,为什么我们需要使用枚举呢?想象一下这样一个场景,您可能希望将网站中用户的性别类型限制为MALE、FEMALE和N/A。当然,字符串列表也能很好地完成这项工作,比如user.gender = 'MALE'或user.gender = 'FEMALE'。然而,使用字符串作为性别属性的值对于程序员错误和恶意攻击来说并不健壮。程序员可以很容易地将“男性”错打成“麦芽酒”,或者将“女性”错打成“电子邮件”,代码仍然会运行,并产生有趣但可怕的结果。攻击者可能会向系统提供精心构建的垃圾字符串值,试图导致系统崩溃或获得根用户访问权限。如果我们使用 enum 将user.gender属性限制在有限的值列表中,上述问题都不会发生。
Python enum 简单易行
在 Python 中,内置函数type接受一个或三个参数。根据 Python 文档,当你传递一个参数给type时,它返回一个对象的类型。当你向type传递三个参数时,比如type(name, bases, dict):
它返回一个新的类型对象。这本质上是一种动态形式的
class语句。第一个参数name字符串是类名。第二个参数basestuple 指定了新类型的基类。第三个参数dict是包含类体定义的名称空间。
使用type,我们可以通过以下方式构建一个enum:
>>> def enum(**named_values):
... return type('Enum', (), named_values)
...
>>> Color = enum(RED='red', GREEN='green', BLUE='blue')
>>> Color.RED
'red'
>>> Color.GREEN
'green'
>>> Gender = enum(MALE='male', FEMALE='female', N_A='n/a')
>>> Gender.N_A
'n/a'
>>> Gender.MALE
'male'
现在Color和Gender是行为类似于enum的类,你可以以如下方式使用它们:
>>> class User(object):
... def __init__(self, gender):
... if gender not in (Gender.MALE, Gender.FEMALE, Gender.N_A):
... raise ValueError('gender not valid')
... self.gender = gender
...
>>> u = User('malicious string')
Traceback (most recent call last):
File "", line 1, in
File "", line 4, in __init__
ValueError: gender not valid
注意传入的无效字符串被User的构造函数拒绝。
Python enum 的奇特方式
尽管前面实现enum的方法很简单,但它确实需要您为每个named value显式指定一个值。例如,在enum(MALE='male', FEMALE='female', N_A='n/a')中,MALE是一个名称,'male'是该名称的值。由于大部分时间我们只通过名称使用enum,我们可以通过以下方式实现自动赋值的enum:
>>> def enum(*args):
... enums = dict(zip(args, range(len(args))))
... return type('Enum', (), enums)
...
>>> Gender = enum('MALE', 'FEMALE', 'N_A')
>>> Gender.N_A
2
>>> Gender.MALE
0
使用zip和range,代码自动为args中的每个name分配一个整数值。
Python 枚举的技巧和建议
enum有助于限制属性值的公开选择,这在现实编程中非常有用。当处理数据库时,在 Python 和数据库管理系统中相同的enum可以防止许多难以发现的错误。- 使用一个
enum来保护你的系统免受恶意输入。在系统接受输入值之前,一定要检查它们。