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)
>>> isinstance(Color.green, Color)
True
这允许您在枚举定义中的成员上定义功能。在值上定义功能可以使用其他先前的方法来完成,但这将是非常不优雅的。
改进:字符串强制
字符串表示是可读的,而repr有更多信息:>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
我发现这是对神奇数字的改进,甚至可能比namedtuple中的字符串更好。
迭代(奇偶校验):
枚举也支持迭代(比如namedtuple,但不太支持裸类):>>> for color in Color:
print(color)
Color.red
Color.green
Color.blue
__members__属性是枚举名称到其各自枚举对象的有序映射(类似于namedtuple的_asdict()函数)。>>> Color.__members__
mappingproxy(OrderedDict([('red', ), ('green', ),
('blue', )]))
支持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公司。
我应该什么时候避开他们?
不要自己滚动或让人们猜测神奇的数字和字符串。不要回避他们。拥抱他们。
但是,如果出于历史原因,您的枚举成员必须是整数,则来自同一个模块的IntEnum具有相同的行为,但也是整数,因为它在子类化Enum之前对内置的int进行子类化。来自IntEnum的帮助:class IntEnum(builtins.int, Enum)
我们可以看到,IntEnum值将作为int的实例进行测试。