Python,什么是枚举类型? [英] Python, what's the Enum type good for?

查看:84
本文介绍了Python,什么是枚举类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Python 3.4中,我们在标准库中获得了一个枚举库:枚举。我们可以获得一个适用于Python 2.4到2.7(甚至3.1到3.3),枚举 pypi / enum34rel =nofollow> enum34 在pypi。



但是,如果没有这个新模块,我们已经设法相处了很久,那么为什么我们现在拥有它?



我有一个关于来自其他语言的枚举的目的的一般想法。在Python中,通常使用如下的裸类,并将其称为枚举:

 类颜色:
blue = 1
green = 2
red = 3

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

  function_of_color(Colors.green)

如果这有任何批评,它是可变的,你不能迭代(容易),我们如何要知道整数的语义, 2



然后我想我可以只使用一个名叫tuple的东西,这将是不可变的?

 >>> Colors = namedtuple('Colors','blue green red')
>>> color = Colors('blue','green','red')
>>>颜色
颜色(蓝色='蓝色',绿色='绿色',红色='红色)
>>>列表(颜色)
['blue','green','red']
>>> len(颜色)
3
>>> colors.blue
'blue'
>>> color.index(colors.blue)
0

创建namedtuple有一点冗余(我们必须写每个名字两次),并且有点不起眼。获得数字的颜色也有点不太客气(我们必须写入颜色两次)。价值检查必须使用字符串来完成,这将不太有效。



所以回到枚举。



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

解决方案


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


枚举类型通过 PEP 435 。给出的推理是:


枚举的属性对于定义一个不可变的相关的一组常量值可能有或可能没有一个语义意义。


当为此使用数字和字符串时,可以将其定义为魔术数字或魔术字符串。数字很​​少携带它们的语义,字符串容易混淆(大写?拼写?蛇或骆驼案?)



星期几和学校信件成绩是例子的这种价值收藏。



以下是 docs

从$枚举导入枚举

class颜色(枚举):
red = 1
green = 2
blue = 3

像裸体类一样,这比可用的例子更可读和优雅,它也是不可变的,它还有其他好处,如下所示。



< h3>严格统治:枚举成员的类型是枚举

 >>> 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,课程):

 >>>颜色颜色:
print(color)
Color.red
Color.green
Color.blue

__ members __ 属性是一个 OrderedDict 映射的名称枚举他们各自的枚举对象(类似于namedtuple的 _asdict()函数)。



由pickle(parity)



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

 >>>进口泡菜
>>> color.red是pickle.loads(pickle.dumps(color.red))
True



改进:别名



这是一个很好的功能,这个裸类没有,很难说这个别名在 namedtuple

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

别名来自规范名称,但它们都是一样的:

 >>> Color.blue是Color.really_blue 
True

如果禁止别名避免价值冲突,使用 enum.unique 装饰器(一个严格的主要特征)。



严格统治:与



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

 >>> Color.red是Color.red 
True
>>> Color.red是Color.blue
False
>>> Color.red不是Color.blue
True

测试平等工作,​​但是

与其他Python类不同的语义


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

 >>> Color.red是颜色('red')

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



枚举未被假定为



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



但是,您可以给您的枚举订单(请参阅下一节)



子类化



您不能对已声明成员的枚举进行子类化,但是您可以子类化不会声明成员共享行为的枚举(请参阅 文档)。



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



我应该使用 enum.Enum



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



在您需要明确指定使用规范名称的代码中使用枚举数据的规范来源的任何地方使用它,而不是任意数据。



例如,如果在您的代码中,您希望用户声明它不是绿色绿色,2或Greene,但 Color.green - 使用enum.Enum对象。这是明确的和具体的。



文档



什么时候我应该避免?



停止滚动自己或让人猜测魔术数字和字符串。不要避开他们拥抱他们



但是,如果您的枚举成员由于历史原因而被要求为整数,则来自同一模块的 IntEnum ,它具有相同的行为,但也是一个整数,因为它在子类枚举之前子类化内置 int 。从 IntEnum 的帮助:

  class IntEnum(builtins.int,枚举)

我们可以看到IntEnum值将作为 int


In Python 3.4, we got an Enum lib in the standard library: enum. We can get a backport for enum that works with Python 2.4 to 2.7 (and even 3.1 to 3.3), enum34 in pypi.

But we've managed to get along for quite some time without this new module - so why do we now have it?

I have a general idea about the purpose of enums from other languages. In Python, it has been common to use a bare class as follows and refer to this as an enum:

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

This can be used in an API to create a canonical representation of the value, e.g.:

function_of_color(Colors.green)

If this has any criticisms, it's mutable, you can't iterate over it (easily), and how are we to know the semantics of the integer, 2?

Then I suppose I could just use something like a namedtuple, which would be immutable?

>>> 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

The creation of the namedtuple is a little redundant (we have to write each name twice), and so somewhat inelegant. Getting the "number" of the color is also a little inelegant (we have to write colors twice). Value checking will have to be done with strings, which will be a little less efficient.

So back to enums.

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?

解决方案

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?

The Enum type got into Python via PEP 435. The reasoning given is:

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.

When using numbers and strings for this purpose, they could be characterized as "magic numbers" or "magic strings". Numbers rarely carry with them the semantics, and strings are easily confused (capitalization? spelling? snake or camel-case?)

Days of the week and school letter grades are examples of this kind of collections of values.

Here's an example from the docs:

from enum import Enum

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

Like the bare class, this is much more readable and elegant than the namedtuple example, it is also immutable, and it has further benefits as we'll see below.

Strictly dominant: The type of the enum member is the enum

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

This allows you to define functionality on the members in the Enum definition. Defining functionality on the values could be accomplished with the other prior methods, but it would be very inelegant.

Improvement: String coercion

The string representation is human readable, while the repr has more information:

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

I find this to be an improvement over the magic numbers and even possibly better than strings from the namedtuple.

Iteration (parity):

The enum supports iteration (like the namedtuple, but not so much the bare class) too:

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

The __members__ attribute is an OrderedDict mapping the names of the enums to their respective enum objects (similar to namedtuple's _asdict() function).

Supported by pickle (parity)

You can serialize and deserialize the enum (in case anyone was worried about this):

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

Improvement: Aliases

This is a nice feature that the bare class doesn't have, and it would be difficult to tell the alias was there in the namedtuple.

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

The alias comes after the canonical name, but they are both the same:

>>> Color.blue is Color.really_blue
True

If aliases should be prohibited to avoid value collisions, use the enum.unique decorator (a strictly dominant feature).

Strictly dominant: comparisons done with is

The enum is intended to be tested with is, which is a fast check for a single object's identity in the process.

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

Tests for equality work as well, but tests for identity with is are optimal.

Different semantics from other Python classes

Enum classes have different semantics from regular Python types. The values of the Enum are instances of the Enum, and are singletons in memory for those values - there is no other purpose for instantiating them.

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

This is important to keep in mind, perhaps it is a downside, but comparing on this dimension is comparing apples with oranges.

Enums not assumed to be ordered

While the Enum class knows what order the members are created in, enums are not assumed to be ordered. This is a feature because many things that may be enumerated have no natural order, and therefore order would be arbitrary.

However, you can give your enums order (see the next section).

Subclassing

You can't subclass an Enum with members declared, but you can subclass an Enum that doesn't declare members to share behavior (see the OrderedEnum recipe in the docs).

This is a feature - it makes little sense to subclass an Enum with members, but again, the comparison is apples and oranges.

When should I use enum.Enum?

This is the new canonical enumeration in Python. Collaborators will expect your enums to behave like these enums.

Use it anywhere you have a canonical source of enumerated data in your code where you want explicitly specified to use the canonical name, instead of arbitrary data.

For example, if in your code you want users to state that it's not "Green", "green", 2, or "Greene", but Color.green - use the enum.Enum object. It's both explicit and specific.

There are a lot of examples and recipes in the documentation.

When should I avoid them?

Stop rolling your own or letting people guess about magic numbers and strings. Don't avoid them. Embrace them.

However, if your enum members are required to be integers for historic reasons, there's the IntEnum from the same module, which has the same behavior, but is also an integer because it subclasses the builtin int before subclassing Enum. From IntEnum's help:

class IntEnum(builtins.int, Enum)

we can see that the IntEnum values would test as an instance of an int.

这篇关于Python,什么是枚举类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆