Python,枚举类型有什么好处?

2024-05-29 03:51:04 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python3.4中,我们在标准库中得到了一个枚举库:enum。我们可以为pypi中与Python 2.4到2.7(甚至3.1到3.3)一起工作的enum获得一个backport。

但是我们在没有这个新模块的情况下已经相处了很长一段时间了-那为什么我们现在有了它?

我对其他语言枚举的目的有一个大致的了解。在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)。值检查必须用字符串来完成,这样效率会低一些。

回到枚举。

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


Tags: 模块目的pypi语言标准情况greenenum
1条回答
网友
1楼 · 发布于 2024-05-29 03:51:04

What's the purpose of enums? What value do they create for the language? When should I use them and when should I avoid them?

枚举类型通过PEP 435进入Python。给出的理由是:

The properties of an enumeration are useful for defining an immutable, related set of constant values that may or may not have a semantic meaning.

当为此目的使用数字和字符串时,它们可以被描述为"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

这允许您在枚举定义中的成员上定义功能。在值上定义功能可以使用其他先前的方法来完成,但这将是非常不优雅的。

改进:字符串强制

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

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

我发现这是对神奇数字的改进,甚至可能比namedtuple中的字符串更好。

迭代(奇偶校验):

枚举也支持迭代(比如namedtuple,但不太支持裸类):

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

__members__属性是枚举名称到其各自枚举对象的有序映射(类似于namedtuple的_asdict()函数)。

>>> Color.__members__
mappingproxy(OrderedDict([('red', <Color.red: 1>), ('green', <Color.green: 2>), 
('blue', <Color.blue: 3>)]))

支持pickle(奇偶校验)

您可以序列化和反序列化枚举(以防有人担心此问题):

>>> 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类型具有不同的语义。枚举的值是枚举的实例,并且是这些值在内存中的单例-没有其他的用途来实例化它们。

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

记住这一点很重要,也许这是一个缺点,但在这个维度上的比较是将苹果与桔子进行比较。

枚举不被认为是有序的

虽然枚举类知道成员的创建顺序,但不假定枚举是按顺序创建的。这是一个特性,因为许多可以枚举的事物没有自然顺序,因此顺序是任意的。

但是,可以给出枚举顺序(请参阅下一节)。

子类化

不能将已声明成员的枚举子类化,但可以将未声明成员以共享行为的枚举子类化(请参见docs中的orderedum配方)。

这是一个特性-用成员对枚举进行子类化是没有意义的,但是同样,比较是苹果和橘子。

我应该什么时候使用enum.Enum

这是Python中新的规范枚举。协作者将期望您的枚举的行为类似于这些枚举。

在代码中任何希望显式指定使用规范名称而不是任意数据的枚举数据规范源处使用它。

例如,如果您希望用户在代码中声明它不是"Green""green"、2或"Greene",而是Color.green-请使用enum.enum对象。它既明确又明确cific公司。

documentation中有很多例子和食谱。

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

不要自己滚动或让人们猜测神奇的数字和字符串。不要回避他们。拥抱他们。

但是,如果出于历史原因,您的枚举成员必须是整数,则来自同一个模块的IntEnum具有相同的行为,但也是整数,因为它在子类化Enum之前对内置的int进行子类化。来自IntEnum的帮助:

class IntEnum(builtins.int, Enum)

我们可以看到,IntEnum值将作为int的实例进行测试。

相关问题 更多 >

    热门问题