Python,Enum 类型有什么用? [英] Python, what's the Enum type good for?

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

问题描述

在 Python 3.4 中,我们在标准库中获得了一个 Enum 库:enum.我们可以获得适用于 Python 2.4 到 2.7(甚至 3.1 到 3.3)的 enum 的反向移植,enum34.

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

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

类颜色:蓝色 = 1绿色 = 2红色 = 3

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

function_of_color(Colors.green)

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

那么我想我可以使用像命名元组这样的东西,它是不可变的?

<预><代码>>>>Colors = namedtuple('颜色', '蓝绿红')>>>颜色 = 颜色('蓝色','绿色','红色')>>>颜色颜色(蓝色=蓝色",绿色=绿色",红色=红色")>>>列表(颜色)['蓝色','绿色','红色']>>>len(颜色)3>>>颜色.蓝色'蓝色'>>>颜色.索引(颜色.蓝色)0

namedtuple 的创建有点多余(我们必须为每个名字写两次),所以有点不优雅.获取颜色的编号"也有点不雅(我们要写两次colors).值检查必须用字符串来完成,这样效率会低一些.

回到枚举.

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

解决方案

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

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

<块引用>

枚举的属性对于定义一组不可变的、相关的常量值很有用,这些常量值可能有也可能没有语义.

当为此目的使用数字和字符串时,它们可以被描述为 magic numbers"; 或魔法字符串".数字很​​少带有语义,字符串很容易混淆(大写?拼写?snake 或camel-case?)

星期几和学校字母等级是这种值集合的例子.

以下是文档的示例:

from enum import Enum类颜色(枚举):红色 = 1绿色 = 2蓝色 = 3

与裸类一样,这比namedtuple示例更具可读性和优雅性,它也是不可变的,并且我们将在下面看到它具有更多好处.

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

<预><代码>>>>类型(颜色.红色)<枚举'颜色'>>>>isinstance(颜色.绿色,颜色)真的

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

改进:字符串强制

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

<预><代码>>>>打印(颜色.红色)红色>>>打印(repr(颜色.红色))<颜色.红色:1>

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

迭代(奇偶校验):

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

<预><代码>>>>对于颜色中的颜色:打印(彩色)红色颜色.绿色颜色.蓝色

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

>>>颜色.__成员__mappingproxy(OrderedDict([('red', ), ('green', ),('蓝色', <Color.blue: 3>)]))

pickle 支持(奇偶校验)

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

<预><代码>>>>进口泡菜>>>color.red 是 pickle.loads(pickle.dumps(color.red))真的

改进:别名

这是裸类所没有的一个很好的特性,而且很难判断namedtuple中是否存在别名.

class 颜色(枚举):红色 = 1绿色 = 2蓝色 = 3真蓝 = 3

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

<预><代码>>>>Color.blue 是 Color.really_blue真的

如果应该禁止别名以避免值冲突,请使用 enum.unique 装饰器(一个严格的主要功能).

严格占优:用is

进行比较

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

<预><代码>>>>Color.red 是 Color.red真的>>>Color.red 是 Color.blue错误的>>>Color.red 不是 Color.blue真的

相等性测试也有效,但使用 is 进行身份测试是最佳的.

与其他 Python 类不同的语义

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

<预><代码>>>>Color.red 是颜色(1)

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

不假定枚举是有序的

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

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

子类化

您不能对声明了成员的 Enum 进行子类化,但可以子类化未声明成员以共享行为的 Enum(请参阅 docs).

这是一个特性 - 用成员子类化一个 Enum 没什么意义,但同样,比较是苹果和橘子.

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

这是 Python 中新的规范枚举.合作者希望您的枚举表现得像这些枚举.

在代码中有枚举数据的规范源的任何地方使用它,您希望明确指定使用规范名称而不是任意数据.

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

文档.

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

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

但是,如果您的枚举成员由于历史原因需要为整数,那么来自同一模块的 IntEnum 具有相同的行为,但也是整数,因为它是内置 IntEnum 的子类code>int 在子类化 Enum 之前.来自 IntEnum 的帮助:

class IntEnum(builtins.int, Enum)

我们可以看到 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 ordered mapping of the names of the enums to their respective enum objects (similar to namedtuple's _asdict() function).

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

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(1)

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,Enum 类型有什么用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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