Python,Enum类型的优点是什么?

时间:2016-06-02 20:36:00

标签: python enums

在Python 3.4中,我们在标准库中获得了一个Enum lib:enum。我们可以获得enum的后端,它适用于Python 2.4到2.7(甚至3.1到3.3),p enum34

但是如果没有这个新模块,我们已经相处了很长时间 - 那么为什么我们现在拥有它呢?

我对其他语言的枚举目的有一个大概的了解。在Python中,通常使用如下的裸类并将其称为枚举:

class Colors:
    blue = 1
    green = 2
    red = 3

这可以在API中用于创建值的规范表示,例如:

function_of_color(Colors.green)

如果这有任何批评,它是可变的,你可以(轻松地)迭代它,我们怎样才能知道整数的语义,2

然后我想我可以使用像namedtuple这样的东西吗?

>>> Colors = namedtuple('Colors', 'blue green red')
>>> colors = Colors('blue', 'green', 'red')
>>> colors
Colors(blue='blue', green='green', red='red')
>>> list(colors)
['blue', 'green', 'red']
>>> len(colors)
3
>>> colors.blue
'blue'
>>> colors.index(colors.blue)
0

namedtuple的创建有点多余(我们必须将每个名称写两次),因此有点不优雅。得到"号码"颜色也有点不雅(我们必须写两次colors)。必须使用字符串进行值检查,效率会稍低。

回到枚举。

枚举的目的是什么?他们为语言创造了什么价值?我什么时候应该使用它们?什么时候应该避免使用它们?

1 个答案:

答案 0 :(得分:53)

  

枚举的目的是什么?他们为语言创造了什么价值?我何时应该使用它们,何时应该避免使用它们?

Enum类型通过PEP 435进入Python。给出的推理是:

  

枚举的属性对于定义可能有或没有语义含义的不可变的相关常量值集非常有用。

为此目的使用数字和字符串时,它们可以表示为"magic numbers"或“魔术字符串”。数字很​​少带有语义,字符串容易混淆(大写?拼写?蛇或骆驼案?)

每周的几天和学校的字母等级都是这类价值观的例子。

以下是docs

的示例
from enum import Enum

class Color(Enum):
    red = 1
    green = 2
    blue = 3

与裸类一样,它比namedtuple示例更具可读性和优雅性,它也是不可变的,它还有其他好处,我们将在下面看到。

严格占优势:枚举成员的类型是枚举

>>> type(Color.red)
<enum 'Color'>
>>> isinstance(Color.green, Color)
True

这允许您在Enum定义中定义成员的功能。使用其他现有方法可以完成对值的功能定义,但它会非常不优雅。

改进:字符串强制

字符串表示是人类可读的,而repr有更多信息:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>

我发现这是对魔术数字的改进,甚至可能比来自namedtuple的字符串更好。

迭代(奇偶校验):

enum支持迭代(比如namedtuple,但不是那么多裸机类):

>>> for color in Color:
        print(color)
Color.red
Color.green
Color.blue

__members__属性是OrderedDict将枚举的名称映射到它们各自的枚举对象(类似于namedtuple的_asdict()函数)。

由pickle(parity)支持

你可以序列化和反序列化枚举(如果有人担心这个):

>>> import pickle
>>> color.red is pickle.loads(pickle.dumps(color.red))
True

改进:别名

这是一个很好的功能,裸类没有,并且很难在namedtuple中告诉别名。

class Color(Enum):
    red = 1
    green = 2
    blue = 3
    really_blue = 3

别名出现在规范名称之后,但它们都是相同的:

>>> Color.blue is Color.really_blue
True

如果禁止使用别名以避免值冲突,请使用enum.unique装饰器(严格占优势的功能)。

严格占优势:与is

进行比较

枚举旨在使用is进行测试,这是对过程中单个对象身份的快速检查。

>>> Color.red is Color.red
True
>>> Color.red is Color.blue
False
>>> Color.red is not Color.blue
True

同样测试平等工作,​​但使用is进行身份测试是最佳的。

与其他Python类不同的语义

枚举类与常规Python类型具有不同的语义。 Enum的值是Enum的实例,并且是内存中用于这些值的单例 - 实例化它们没有其他目的。

>>> Color.red is Color('red')

重要的是要记住,也许这是一个缺点,但在这方面的比较是将苹果与橙子进行比较。

不假定订购的枚举

虽然Enum类知道创建成员的顺序,但不假定枚举是有序的。这是一个特征,因为许多可能枚举的东西没有自然顺序,因此顺序是任意的。

但是,你可以给你的枚举命令(见下一节)。

子类

您不能使用已声明的成员对Enum进行子类化,但是可以继承未声明成员共享行为的Enum(请参阅docs中的OrderedEnum配方)。

这是一个功能 - 将Enum与成员子类化是没有意义的,但同样,比较是苹果和橙子。

我应该何时使用enum.Enum

这是Python中新的规范枚举。合作者希望您的枚举行为与这些枚举类似。

在您希望明确指定使用规范名称而不是任意数据的代码中具有规范数据源的任何位置使用它。

例如,如果在您的代码中,您希望用户声明它不是"Green""green",2或"Greene",而是Color.green - 请使用枚举。枚举对象。它既明确又具体。

documentation中有很多示例和配方。

我什么时候应该避开它们?

停止自己动手或让人们猜测魔法数字和字符串。不要避免它们。拥抱他们。

但是,如果由于历史原因要求枚举成员是整数,那么同一模块中的IntEnum具有相同的行为,但也是一个整数,因为它是内置{{1}的子类。在继承int之前。来自Enum的帮助:

IntEnum

我们可以看到IntEnum值将作为class IntEnum(builtins.int, Enum) 的实例进行测试。