Python,Enum 类型有什么用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37601644/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python, what's the Enum type good for?
提问by Aaron Hall
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), enum34in pypi.
在 Python 3.4 中,我们在标准库中获得了一个 Enum 库:enum
. 我们可以获得enum
适用于 Python 2.4 到 2.7(甚至 3.1 到 3.3)的backport,pypi 中的enum34。
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:
我对来自其他语言的枚举的目的有一个大致的了解。在 Python 中,通常使用如下的裸类并将其称为枚举:
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.:
这可以在 API 中用于创建值的规范表示,例如:
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
?
如果这有任何批评,它是可变的,您无法(轻松)迭代它,我们如何知道整数的语义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.
namedtuple 的创建有点多余(我们必须将每个名称写两次),因此有些不雅。获取颜色的“编号”也有点不雅(我们要写colors
两遍)。值检查必须用字符串来完成,这样效率会低一些。
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?
枚举的目的是什么?他们为语言创造了什么价值?我什么时候应该使用它们,什么时候应该避免它们?
回答by Aaron Hall
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:
Enum 类型通过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.
枚举的属性对于定义一组不可变的、相关的常量值很有用,这些常量值可能有也可能没有语义。
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?)
当为此目的使用数字和字符串时,它们可以被描述为“魔术数字”或“魔术字符串”。数字很少带有语义,字符串很容易混淆(大写?拼写?snake 或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.
与裸类一样,这比 namedtuple 示例更具可读性和优雅性,它也是不可变的,并且我们将在下面看到它的更多好处。
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:
字符串表示是人类可读的,而 repr 有更多信息:
>>> 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.
我发现这是对幻数的改进,甚至可能比来自 namedtuple 的字符串更好。
Iteration (parity):
迭代(奇偶校验):
The enum supports iteration (like the namedtuple, but not so much the bare class) too:
枚举也支持迭代(如namedtuple,但不是裸类):
>>> 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).
该__members__
属性是枚举名称到其各自枚举对象的有序映射(类似于 namedtuple 的_asdict()
功能)。
>>> 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
.
这是裸类所没有的一个很好的特性,而且很难判断别名是否存在于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).
如果应该禁止别名以避免值冲突,请使用enum.unique
装饰器(一个严格的主导功能)。
Strictly dominant: comparisons done with is
严格占优:与 is
The enum is intended to be tested with is
, which is a fast check for a single object's identity in the process.
枚举旨在使用 进行测试is
,这是对流程中单个对象身份的快速检查。
>>> 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.
平等测试也有效,但身份测试is
是最佳的。
Different semantics from other Python classes
与其他 Python 类不同的语义
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.
枚举类与常规 Python 类型具有不同的语义。Enum 的值是 Enum 的实例,并且是这些值在内存中的单例 - 实例化它们没有其他目的。
>>> 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.
虽然 Enum 类知道成员的创建顺序,但不假定枚举是有序的。这是一个特性,因为许多可以枚举的事物没有自然顺序,因此顺序是任意的。
However, you can give your enums order (see the next section).
但是,您可以给出枚举顺序(请参阅下一节)。
Subclassing
子类化
You can't subclass an Enum with members declared, but you cansubclass an Enum that doesn't declare members to share behavior (see the OrderedEnum recipe in the docs).
您不能对声明了成员的 Enum 进行子类化,但可以对未声明成员以共享行为的 Enum进行子类化(请参阅文档中的 OrderedEnum 秘诀)。
This is a feature - it makes little sense to subclass an Enum with members, but again, the comparison is apples and oranges.
这是一个特性——用成员子类化一个 Enum 没什么意义,但同样,比较是苹果和橘子。
When should I use enum.Enum
?
我应该什么时候使用enum.Enum
?
This is the new canonical enumeration in Python. Collaborators will expect your enums to behave like these enums.
这是 Python 中新的规范枚举。合作者会期望你的枚举表现得像这些枚举。
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.
例如,如果在您的代码中您希望用户声明它不是"Green"
, "green"
, 2, or "Greene"
, but Color.green
- 使用 enum.Enum 对象。它既明确又具体。
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:
但是,如果您的枚举成员由于历史原因需要是整数,那么IntEnum
来自同一个模块的 具有相同的行为,但也是整数,因为它int
在子类化之前对内置函数进行了子类化Enum
。来自IntEnum
的帮助:
class IntEnum(builtins.int, Enum)
we can see that the IntEnum values would test as an instance of an int
.
我们可以看到 IntEnum 值将作为int
.