[python] Python枚举模块enum总结

Posted 落痕的寒假

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[python] Python枚举模块enum总结相关的知识,希望对你有一定的参考价值。

枚举是一种数据类型,在编程中用于表示一组相关的常量。枚举中的每个常量都有一个名称和一个对应的值,可以用于增强代码的可读性和可维护性。在Python中,枚举是由enum模块提供的,而不是Python提供专用的枚举语法。关于enum模块介绍见:enum。如需详细了解Python的enum模块,参见文章:Python enum枚举(enum)介绍

1 语法介绍

基础示例

# 引入 Enum 模块,用于创建枚举
from enum import Enum

# 创建一个枚举类Color,从Python内置的枚举类Enum继承
class Color(Enum):
    # 定义 RED 数值为 1
    RED = 1
    # 定义 GREEN 数值为 2
    GREEN = 2
    # 定义 BLUE 数值为 3
    BLUE = 3


col = Color.RED
# 输出Color.RED
print(col)

if col == Color.RED:
    # 判断 col 是否为 Color.RED,若是则输出 "Red"
    print("Red")

# 输出 Color 枚举类的所有成员:[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
print(list(Color))

# 判断col是否为Color类型
print(isinstance(col, Color))

# 输出 col 的类型:<enum \'Color\'>
print(type(col))

# 输出 col 的字符串表示形式:<Color.RED: 1>
print(repr(col))

# 通过 Color[\'RED\'] 获取 Color.RED
print(Color[\'RED\'])

# 通过 Color(1) 获取 Color.RED
print(Color(1))

Color.RED
Red
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]
True
<enum \'Color\'>
<Color.RED: 1>
Color.RED
Color.RED

函数式API创建枚举类

# 引入枚举类型
from enum import Enum

# 使用functional API创建枚举类,定义三个枚举常量,分别为 RED, GREEN, BLUE,从数字2开始标号
Color = Enum(\'Color\', \'RED GREEN BLUE\', start=2)
# 或使用以下形式
# Color = Enum(\'Color\', [(\'RED\', 1), (\'BLUE\', 2), (\'GREEN\', 3)])

# 将 col 设为 GREEN
col = Color.GREEN

# 输出 col 的值:Color.GREEN
print(col)

# 比较 col 和 GREEN 是否相等,如果相等则输出 "Green"
if col == Color.GREEN:
    print("Green")

# 遍历枚举
for color in Color:
    print(color)
# 使用 name 和 value 属性获取枚举名称和值
for color in Color:
    print(color.name, color.value)

# 使用 __members__ 属性获取枚举类型 Color 中的所有枚举常量和名称
for name, member in Color.__members__.items():
    print(name, member)
Color.GREEN
Green
Color.RED
Color.GREEN
Color.BLUE
RED 2
GREEN 3
BLUE 4
RED Color.RED
GREEN Color.GREEN
BLUE Color.BLUE

自动赋值

# 导入枚举和自动赋值模块
from enum import Enum, auto

# 定义一个颜色的枚举类
class Color(Enum):
    # 自动赋值,RED 的值为 1,后面的枚举类值依次加 1
    RED = auto()
    BLUE = auto()
    GREEN = auto()

# 遍历 Color 枚举类,输出每个枚举值
for color in Color:
    print(color)

# 遍历 Color 枚举类,输出每个枚举值的名称和值
for color in Color:
    print(color.name, color.value)

Color.RED
Color.BLUE
Color.GREEN
RED 1
BLUE 2
GREEN 3

异类值

from enum import Enum

# 枚举成员的值类型可以不一样,但是非常不推荐这样做
class UserResponse(Enum):
    YES = 1
    NO = "No"
    MAYBE = "Maybe"

print(list(UserResponse))
[<UserResponse.YES: 1>, <UserResponse.NO: \'No\'>, <UserResponse.MAYBE: \'Maybe\'>]

混合枚举

from enum import Enum

# 通过多重继承创建支持整数比较的枚举
class Size(int, Enum):
    S = 1
    M = 2
    L = 3
    XL = 4

print(Size.S > Size.M)
False
# 实现和上述代码一样的功能
from enum import IntEnum

# 通过IntEnum创建支持整数比较的枚举
class Size(IntEnum):
    S = 1
    M = 2
    L = 3
    XL = 4

print(Size.S > Size.M)
False

强制唯一值

from enum import Enum, unique

# 当成员值不唯一,如果不添加unique装饰器,则代码运行成功,但只保留第一个出现具有相同值的成员。
# 如果添加unique,则运行报错
# @unique
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3
    ORANGE = 3
    WHITE = 3

for color in Color:
    print(color)
Color.RED
Color.GREEN
Color.BLUE

基于位掩码的枚举类型enum.Flag

from enum import IntFlag, Flag, auto

# 创建基于位掩码的枚举类型,注意成员值为2的幂,最好不要自定义
# 如果想创建整数枚举继承IntFlag即可
class Permissions(Flag):
    READ = auto()  # 定义读权限
    WRITE = auto()  # 定义写权限
    EXECUTE = auto()  # 定义执行权限
    DELETE = auto()  # 定义删除权限

# 使用 name 和 value 属性获取枚举名称和值
# 可以看到各个成员的值是2的幂
for p in Permissions:
    print(p.name, p.value)

# 使用枚举成员
perms_rw = Permissions.READ | Permissions.WRITE  # 用户拥有读和写权限
# 可以看到perms_rw的值为3
print(perms_rw.name,perms_rw.value)

# 检查是否有某个权限
# 使用&运算符来判断一个枚举值中是否包含某个特定的枚举值
if perms_rw & Permissions.READ:  # 如果用户拥有读权限
    print("用户拥有读权限")
if perms_rw & Permissions.WRITE:  # 如果用户拥有写权限
    print("用户拥有写权限")
if perms_rw & Permissions.EXECUTE:  # 如果用户拥有执行权限
    print("用户拥有执行权限")

 # 遍历所有权限
for perm in Permissions: 
    print(perm)  
READ 1
WRITE 2
EXECUTE 4
DELETE 8
None 3
用户拥有读权限
用户拥有写权限
Permissions.READ
Permissions.WRITE
Permissions.EXECUTE
Permissions.DELETE

2 参考

Python,枚举类型有啥用? [复制]

【中文标题】Python,枚举类型有啥用? [复制]【英文标题】:Python, what's the Enum type good for? [duplicate]Python,枚举类型有什么用? [复制] 【发布时间】:2016-10-02 18:26:32 【问题描述】:

在 Python 3.4 中,我们在标准库中获得了一个 Enum 库:enum。我们可以在 pypi 中为enum 获得一个支持 Python 2.4 到 2.7(甚至是 3.1 到 3.3)、enum34 的反向移植。

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

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

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

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

function_of_color(Colors.green)

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

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

>>> 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)。值检查必须用字符串来完成,效率会低一些。

回到枚举。

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

【问题讨论】:

嗯,那么什么时候使用namedtuple 更好呢?我有一个案例,我需要一个常量,比如说,字符串表示的集合RANKING_CRITERIA = ['count', 'pair_count', 'freq'],我不想通过数字 ID 来引用它,因为它在代码中是不可理解的,但是字典不是一个选项,因为它是可变的并且不保证.keys() 的顺序。似乎namedtuple 是我的情况。还有其他想法吗? @alisa 何时使用命名元组?当您需要不可变记录并且不太需要继承时使用它们。但是,现在默认情况下对字典进行排序,因此 .keys() 将通过插入进行排序。更多的上下文将帮助我帮助你。你到底想做什么,为什么要这样做?字符串列表可能是最恰当的。 【参考方案1】:

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

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

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

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

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

改进:字符串强制

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

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

我发现这是对幻数的改进,甚至可能比命名元组中的字符串更好。

迭代(奇偶校验):

枚举也支持迭代(像 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 类型具有不同的语义。 Enum 的值是 Enum 的实例,并且是这些值在内存中的单例 - 实例化它们没有其他目的。

>>> Color.red is Color(1)

记住这一点很重要,也许这是一个缺点,但在这个维度上比较是比较苹果和橙子。

枚举不假定是有序的

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

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

子类化

您不能对声明了成员的 Enum 进行子类化,但您可以对未声明成员的 Enum 进行子类化以共享行为(请参阅 docs 中的 OrderedEnum 配方)。 p>

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

什么时候应该使用enum.Enum

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

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

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

documentation中有很多例子和食谱。

什么时候应该避开它们?

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

但是,如果您的枚举成员因历史原因需要为整数,则来自同一模块的 IntEnum 具有相同的行为,但也是一个整数,因为它在子类化 @ 之前继承了内置 int 987654352@。来自IntEnum的帮助:

class IntEnum(builtins.int, Enum)

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

【讨论】:

非常感谢这个全面的答案。在我看来,现在可以用文字类型python.org/dev/peps/pep-0586 替换(完全?)枚举 - 对此有何想法? 我看不到枚举是如何被文字类型替换的。你能帮我把这些点联系起来吗? 我想我可能会收回该声明中更全面(即“完全”)的部分 :) 但作为这种替换的一个例子,我们使用了很多 class SomeCollection(str, Enum) 类,直到我们找到文字。替换了这些之后,我们 a) 认为它更易于阅读(字符串现在看起来像字符串,而不是类的属性),并且 b) 我们可以组合它们,例如LargerCollection = Literal[SmallCollection1, SmallCollection2],我们不能用 Enums 做到这一点(这对我们来说是最相关的原因)。非常感谢这是一个特殊情况,但并不普遍。 取自您的回答; “定义一组不可变的、相关的常量值,这些常量值可能具有也可能不具有语义含义”,来自您的回答。也许有人会说 Literals 是“定义一组不可变的、相关的 allowed 常量值,这些常量值可能有也可能没有语义含义”?因此,它们明显不同,但相关 - 似乎合理且完全有用? :) 找不到专门针对 python 的“Enum vs Literal Type”的好讨论,但这篇文章是针对 TypeScript 的,有些人可能仍然觉得有用:***.com/questions/49761972/…

以上是关于[python] Python枚举模块enum总结的主要内容,如果未能解决你的问题,请参考以下文章

Python 的枚举 Enum

Python 枚举

Python,枚举类型有啥用? [复制]

Python枚举的定义和使用(enum)

pydantic学习与使用-9.枚举类型(enum)

python常用模块 enum