enum -- 枚举

1,664 阅读14分钟

前情提示: 测试代码中,右尖括号(>)表示命令行中输入的命令; 单独一行并以井字符(#)开头的为输出内容; 库的导入仅在本文的第一个测试代码中展现,其他代码块均省略库的导入代码。

  • 系统类型: Windows 10
  • python 版本: Python 3.9.0

枚举是一组符号名称(枚举成员)的集合,枚举成员应该是唯一的、不可变的。

enum 模块将分为三个部分解析,第一部分主要介绍枚举的特性和 Enum 类,第二部分将介绍 Enum 类的三个变种类及自定义类,第三部分将更深入的了解枚举。也可通过传送门查看微信公众号文章。

传送门

enum -- 枚举(一)

enum -- 枚举(二)

enum -- 枚举(三)

第一部分

创建一个 Enum

枚举是使用 class 语法来创建的,这使得它们易于读写。但还是有一种以使用函数形式的创建方式,这个留到下文再讲,现在主要来说明一下使用 class 语法的创建方式。

import enum

class Test(enum.Enum):
    A = 1
    B = 'b'

上面的代码创建了一个枚举,class Test 是一个枚举,其中 Test 就是枚举对象的名称。

A = 1B = 'b' 这些等号两边的整体被称为 枚举成员,等号左边的被称为枚举成员的名称,枚举通常是用来表示常量的,所以枚举成员的名称一般使用大写形式。等号右边的被称为枚举成员的值,枚举成员的值可以是任意类型,如 intstr等等,只需要注意枚举成员的值在 枚举对象中是唯一的。

虽然创建枚举时使用了 class 语法,但是枚举与普通的 python 类不同,可以打印一些信息来进行对比:

'''枚举'''
class Test(enum.Enum):
    A = 1

print(Test)                      # 打印枚举
# <enum 'Test'>
print(Test.A)                    # 打印枚举成员本身
# Test.A
print(repr(Test.A))              # 打印枚举成员在解释器中的形式
# <Test.A: 1>
print(type(Test.A))              # 打印枚举成员类型
# <enum 'Test'>
print(isinstance(Test.A, Test))  # 判断枚举成员类型是否有枚举类型一致
# True

'''普通的 python 类'''
class Common:
    A = 1

print(Common)                    # 打印类
# <class '__main__.Common'>
print(Common.A)                  # 打印类属性的值
# 1
print(repr(Common.A))            # 打印类属性的值在解释器中的形式
# 1
print(type(Common.A))            # 打印类属性的值的类型
# <class 'int'>
print(isinstance(Common.A, Test))  # 判断类属性的值的类型是否有类的类型一致
# False

首先,枚举创建出的对象自成一种类型,类型名就是枚举的名称(类名)。枚举的枚举成员(类的属性)被创建成了一个对象,并且类型就是所属枚举的类型。

枚举可以按照定义顺序进行迭代,并且枚举成员是不可变的,是可哈希的,因此字典中的键可以使用枚举成员。

class Test(enum.Enum):
    A = 1

test_dict = {}
for i in Test:
    test_dict[i] = 'value.' + str(i)
print(test_dict)
# {<Test.A: 1>: 'value.Test.A', <Test.B: 'b'>: 'value.Test.B', <Test.C: 'c'>: 'value.Test.C'}
print(test_dict[Test.A])
# value.Test.A

那么怎么获取枚举成员呢,在枚举中有很多种访问方式:

class Test(enum.Enum):
    A = 1

print(Test(1))       # 程序化访问
# Test.A
print(Test['A'])     # 条目访问
# Test.A
print(Test.A.value)  # 枚举成员的 value 属性
# 1
print(Test.A.name)   # 枚举成员的 name 属性
# A

枚举中不允许有相同的枚举成员,但是允许枚举成员有同样的值,若查找的值有多个枚举成员,那么会按定义顺序返回第一个。

'''枚举成员有相同的名称'''
class Test(enum.Enum):
    A = 1
    A = 2
# TypeError: Attempted to reuse key: 'A'

'''枚举成员有相同的值'''
class Test(enum.Enum):
    A = 1
    B = 1

print(Test(1))
# Test.A

enum 模块中定义了一个装饰器 -- @enum.unique,可以强制规定枚举成员的值也必须是唯一的。

@enum.unique
class Test(enum.Enum):
    A = 1
    B = 1
# ValueError: duplicate values found in <enum 'Test'>: B -> A

若枚举成员的值不重要或者其他不需要一一指定枚举成员的场景下,可以使用 enum.auto() 方法来替代输入的值。

class Test(enum.Enum):
    A = enum.auto()
    B = enum.auto()
    C = 1

print(repr(Test.A))
# <Test.A: 1>
print(repr(Test.B))
# <Test.B: 2>
print(repr(Test.C))
# <Test.A: 1>
print(list(Test))
# [<Test.A: 1>, <Test.B: 2>]

当使用 enum.auto() 来定义枚举成员的值时,再次使用 int 类型的值时,最后得到的结果可能会和预想的不太一样。

enum.auto() 函数的返回值是由 _generate_next_value_() 函数决定的,默认情况下,此函数是根据最后一个 int 类型的枚举成员的值增加 1。此函数是可以重载的,重载时,方法定义必须在任何成员之前。

'''先定义一个值为 int 类型的枚举成员, 再使用 auto() 函数'''
class Test(enum.Enum):
    A = 2
    B = enum.auto()
    C = enum.auto()
print(list(Test))
# [<Test.A: 2>, <Test.B: 3>, <Test.C: 4>]

'''定义一个值为非 int 类型的枚举成员, 再使用 auto() 函数'''
class Test(enum.Enum):
    A = 'b'
    B = enum.auto()
    C = enum.auto()
print(list(Test))
# [<Test.A: 'b'>, <Test.B: 1>, <Test.C: 2>]

'''重载 _generate_next_value_()'''
class Test(enum.Enum):
    def _generate_next_value_(name, start, count, last_values):
        return name  # 返回枚举成员的名字

    A = enum.auto()
    B = enum.auto()

print(list(Test))
# [<Test.A: 'A'>, <Test.B: 'B'>]

在介绍 enum.auto() 函数时,测试示例中,枚举对象转换为 list 类型时少了一个枚举成员,这是因为少的那个枚举成员与之前的枚举成员的值是一样的,这个枚举成员被认为是一个别名,当对枚举成员迭代时,不会给出别名。

class Test(enum.Enum):
    A = 1
    B = 1
print(list(Test))
# [<Test.A: 1>]

而特殊属性 __members__是一个从名称到成员的只读有序映射。它包含枚举中所有的枚举成员,但是在迭代时,因为别名的原因,指向的枚举成员还是该值第一个定义的枚举成员。

class Test(enum.Enum):
    A = 1
    B = 1
print(dict(Test.__members__))
# {'A': <Test.A: 1>, 'B': <Test.A: 1>}

小栗子,找出枚举中所有的别名:

class Test(enum.Enum):
    A = 1
    B = 1
    C = 2
    D = 2
print([名称 for 名称, 枚举成员 in Test.__members__.items() if 枚举成员.name != 名称])
# ['B', 'D']

在枚举中,不仅仅只可以定义一些枚举成员,枚举也属于 python 的类,是可以拥有普通方法和特殊方法的,这里列举一个文档上的示例:

class Mood(enum.Enum):
    FUNKY = 1
    HAPPY = 3

    def describe(self):
        return self.name, self.value

    def __str__(self):
        return 'my custom str! {0}'.format(self.value)

    @classmethod
    def favorite_mood(cls):
        return cls.HAPPY

print(Mood.favorite_mood())
# my custom str! 3
print(Mood.HAPPY.describe())
# ('HAPPY', 3)
print(str(Mood.FUNKY))
# my custom str! 1

但是枚举还是有一些限制,以单下划线开头和结尾的名称是由枚举保留而不可使用,例外项是包括特殊方法成员 (__str__()__add__() 等),描述符 (方法也属于描述符) 以及在 _ignore_ 中列出的变量名。

在介绍 _generate_next_value_() 函数时,为了重载这个函数,我们创建了一个基于 enum.Enum 的枚举,可以成为 枚举A,重载了 _generate_next_value_() 函数,然后又创建了一个基于 枚举A 的枚举。这样的定义是被允许的,在 enum 模块的规定下,一个新的 Enum 类必须基于一个 Enum 类,至多一个实体数据类型以及出于实际需要的任意多个基于 objectmixin 类。 这些基类的顺序为:

class EnumName([mix-in, ...,] [data-type,] base-enum):
    pass

另外,没有定义枚举成员的枚举才能被其他枚举继承,否则将会报错:

class A(enum.Enum):
    AA = 1
class B(A):
    BB = 2
# TypeError: B: cannot extend enumeration 'A'

第二部分

IntEnum

IntEnum 类是 Enum 类的子类,也是 int 的子类。IntEnum 的枚举成员的值必须是 int 类型,填写其他类型会报错。

class IntTest(enum.IntEnum):
    A = 1
    B = 'b'
# ValueError: invalid literal for int() with base 10: 'b'

除此之外,IntEnum 类和 Enum 类最大的区别在与比较时的差异,前文中并没有介绍 Enum 类比较功能的特性,这里先说一下 Enum 类的比较:

枚举成员只与自己本身和别名相等,枚举成员与非枚举值比较永远不相等,枚举成员只能无法进行排序比较。

class TestA(enum.Enum):
    A = 1
    B = 1
class TestB(enum.Enum):
    A = 1
    B = 2

print(TestA.A is TestB.A)  # 不同枚举的同名同值的枚举成员的 is 比较
# False
print(TestA.A is TestA.B)  # 同一枚举的同值枚举成员 is 比较(别名)
# True
print(TestA.A == TestB.A)  # 不同枚举的同名同值的枚举成员的 == 比较
# False
print(TestA.A == TestA.B)  # 同一枚举的同值枚举成员 == 比较(别名)
print(TestA.A == 1)  # 与 int 类型比较
# False
print(TestA.A < TestA.B)  # 排序比较
# Traceback (most recent call last):
#   File "e:\project\test\test.py", line 15, in <module>
#     print(TestA.A < TestA.B)
# TypeError: '<' not supported between instances of 'TestA' and 'TestA'

IntEnum 的枚举成员可以直接与 int 类型的值比较,也支持排序比较,并且不同枚举的同名同值的枚举成员使用 == 比较时也相等。

class IntTestA(enum.IntEnum):
    A = 1
    B = 1

class IntTestB(enum.IntEnum):
    A = 1
    B = 2

print(IntTestA.A is IntTestB.A)  # 不同枚举的同名同值的枚举成员的 is 比较
# False
print(IntTestA.A is IntTestA.B)  # 同一枚举的同值枚举成员 is 比较(别名)
# True
print(IntTestA.A == IntTestB.A)  # 不同枚举的同名同值的枚举成员的 == 比较
# True
print(IntTestA.A == IntTestA.B)  # 同一枚举的同值枚举成员 == 比较(别名)
# True
print(IntTestA.A == 1)  # 与 int 类型比较
# True
print(IntTestA.A < IntTestB.B)  # 排序比较
# True

IntEnum 在其他方面的行为类似于 int 类型的数据:

class IntTestA(enum.IntEnum):
    A = 1
    B = 2

print(['a', 'b', 'c'][IntTestA.A])  # 枚举成员作为索引
# b
print([i for i in range(IntTestA.B)])  # 枚举成员作为 range() 函数的参数
# [0, 1]

Flag

Flag 类是 Enum 类的子类,Flag 类与 Enum 类的差别就是 Flag 枚举成员可以使用按位运算符(&|^~)。

class TestFlag(enum.Flag):
    A = 1
    B = 2
    C = A | B

print(list(TestFlag))  # 所有枚举成员
# [<TestFlag.A: 1>, <TestFlag.B: 2>, <TestFlag.C: 3>]

print(repr(TestFlag.A | TestFlag.B))  # 枚举成员A 与 B 按位或
# <TestFlag.C: 3>
print(repr(TestFlag.C))  # 枚举成员 C
# <TestFlag.C: 3>
print(1 | 2)  # 1 与 2 按位或
# 3

Flag 类的枚举成员可以在定义时使用按位运算符,也可以在使用枚举成员时使用按位运算符, 并且如果得到的值与枚举中的枚举成员的值相等时,最后得到的结果就是值与结果相同的枚举成员。那么如果在使用枚举成员时使用按位运算符,最后的结果在枚举中没有枚举成员的值与之相等呢?

class TestFlag(enum.Flag):
    A = 1
    B = 2

print(repr(TestFlag.A | TestFlag.B))  # 枚举成员A 与 B 按位或
# <TestFlag.B|A: 3>
print(type(TestFlag.A | TestFlag.B))  # 查看结果的类型
# <enum 'TestFlag'>

Flag 类的枚举成员之间使用按位运算符之后, 如果枚举中有相同值的枚举成员时得到那个枚举成员,否则将获取一个新的枚举成员。

IntFlag

IntFlag 类是 FlagInt 类的子类。其实,IntFlag 类更像是可以使用按位运算符的 IntEnum。但在某些方面 IntFlag 还是有自己独有的特点。

首先, IntFlag 的枚举成员的值只能是数字,这点与 IntEnum 类相同。IntFlag 的枚举成员在定义时可以使用按位运算符,在使用枚举成员时也可以使用按位运算符, 得到的结果还是枚举成员,这点与 Flag 类相同。

class TestIntFlag(enum.IntFlag):
    A = 1
    B = 2
    C = A | B

print(repr(TestIntFlag.A | TestIntFlag.B))
# <TestIntFlag.C: 3>

因为继承了 Int 类,IntFlag 类的枚举成员也可以像 IntEnum 类的枚举成员那样进行运算,作索引值等等。但是,IntFlag 类的枚举成员进行除按位运算以外的其他运算都将导致失去 IntFlag 类的枚举成员的资格。

class TestIntFlag(enum.IntFlag):
    A = 1
    B = 2

print(TestIntFlag.A + TestIntFlag.B)  # 加法运算
# 3
print(['a', 'b', 'c', 'd'][TestIntFlag.A])  # 索引值
# b

自定义类

当以上枚举类都无法满足需求时,可以修改枚举类中的魔法方法来改变某些特性或功能。

__new__() 魔法方法在每个枚举成员定义前调用,可以修改枚举成员的值和设置自定义属性。

'''修改枚举成员定义的值'''
class Test(enum.Enum):
    A = 'aaa'
    B = 'bbb'

    def __new__(cls, value):
        obj = object.__new__(cls)
        obj._value_ = str(value) + '...'
        return obj

print(list(Test))
# [<Test.A: 'aaa...'>, <Test.B: 'bbb...'>]

__init__() 魔法方法在枚举成员定义后调用,可实现的功能与 __new__() 魔法方法基本一致。这里官方文档中标明出了一点,虽然 __init__() 魔法方法可以定义枚举成员的值,但是不允许使用 __init__() 魔法方法定义枚举成员的值。

'''被官方文档禁止的操作, 修改枚举成员的值'''
class Test(enum.Enum):
    A = 'aaa'
    B = 'bbb'

    def __init__(self, pantone='unknown'):
        self._value_ = str(self.value) + '...'

print(list(Test))
# [<Test.A: 'aaa...'>, <Test.B: 'bbb...'>]

__repr__() 魔法方法的重定义,用来修改使用 repr() 函数时返回的内容:

'''使用 repr() 函数时, 不展示枚举成员的值'''
class TestA(enum.Enum):
    A = enum.auto()
    B = enum.auto()

    def __repr__(self):
        return '<%s.%s>' % (self.__class__.__name__, self.name)


print(repr(TestA.A))
# <TestA.A>
print(TestA.A.value)  # 正常获取枚举成员的值
# 1

__repr__() 魔法方法类似,__str__()__add__()__format__()__bool__() 等等魔法方法都是可以重新定义的。

下面是官方文档中的两个有趣的小例子:

'''一个有序枚举,它不是基于 IntEnum,因此保持了正常的 Enum 不变特性'''
class OrderedEnum(enum.Enum):
    def __ge__(self, other):
        if self.__class__ is other.__class__:
            return self.value >= other.value
        return NotImplemented

    def __gt__(self, other):
        if self.__class__ is other.__class__:
            return self.value > other.value
        return NotImplemented

    def __le__(self, other):
        if self.__class__ is other.__class__:
            return self.value <= other.value
        return NotImplemented

    def __lt__(self, other):
        if self.__class__ is other.__class__:
            return self.value < other.value
        return NotImplemented

class Grade(OrderedEnum):
    A = 5
    B = 4
    C = 3
    D = 2
    F = 1

print(Grade.C < Grade.A)
# True


'''如果发现重复的成员名称则将引发错误而不是创建别名'''
class DuplicateFreeEnum(enum.Enum):
    def __init__(self, *args):
        cls = self.__class__
        if any(self.value == e.value for e in cls):
            a = self.name
            e = cls(self.value).name
            raise ValueError(
                "aliases not allowed in DuplicateFreeEnum:  %r --> %r"
                % (a, e))

class Color(DuplicateFreeEnum):
    RED = 1
    GREEN = 2
    BLUE = 3
    GRENE = 2
# ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'

第三部分

不重要的枚举成员的值

在某些场景中,人们并不关心枚举成员的值是什么,可以使用下列几种方法定义枚举:

  • 使用 enum.auto() 函数作为枚举成员的值
  • 使用 object() 作为枚举成员的值
  • 使用描述性的字符作为枚举成员的值,同时做到了可进一步解释此枚举成员
  • 使用空元组作为值,并在自定义的 __new__() 中重新定义枚举成员的值(只要不重复即可)

看完以上的列举,这种实现这种需求并不局限于上面几种方法,只要最终枚举成员的值不重复即可,比如自定义一个随机生成字符串的函数。

import enum
import pickle  # 封存


'''enum.auto()'''
class TestA(enum.Enum):
    A = enum.auto()
    B = enum.auto()

'''object()'''
class TestB(enum.Enum):
    A = object()
    B = object()

'''描述字符串'''
class TestC(enum.Enum):
    A = '字母A'
    B = '字母B'

'''元组, 并自定义 __new__()'''
class TestD(enum.Enum):
    A = ()
    B = ()

    def __new__(cls, *args):
        '''获取当前枚举成员数并加一, 将结果赋值给新定义的枚举成员'''
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)  # 必须创建一个枚举成员对象
        obj._value_ = value
        return obj  # 必须返回一个枚举成员对象

print(list(TestA))
# [<TestA.A: 1>, <TestA.B: 2>]
print(list(TestB))
# [<TestB.A: <object object at 0x0000023986178210>>, <TestB.B: <object object at 0x0000023986178220>>]
print(list(TestC))
# [<TestC.A: '字母A'>, <TestC.B: '字母B'>]
print(list(TestD))
# [<TestD.A: 1>, <TestD.B: 2>]

封存

枚举可以被封存和解封:

class Test(enum.Enum):
    A = enum.auto()
    B = enum.auto()

封存的枚举成员A = pickle.dumps(Test.A)
解封的枚举成员A = pickle.loads(封存的枚举成员A)

print(封存的枚举成员A)
# b'\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04Test\x94\x93\x94K\x01\x85\x94R\x94.'
print(解封的枚举成员A)
# Test.A
print(Test.A is 解封的枚举成员A)  # 解封后的数据与原数据是否一致
# True

功能性API

Enum 类属于可调用对象,也就是说 Enum 类可以像函数一样被调用:

Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)

参数:
    value: str, 枚举的名称
    names: 枚举成员们的名称, 可使用多种方式设置枚举成员
    module: 关键字参数, strNone, 指明所在的模块
    qualname: 关键字参数, str, 指明所在的模块中的具体位置
    type: 关键字参数, 指明枚举类的类型
    start: 关键字参数, int, 枚举成员的起始值, 默认为 1
返回值:
    枚举

names 参数可以传入一个以空格或逗号隔开的字符串,分隔的子字符串就是枚举成员的名称,各枚举成员的值按顺序自增,起始值由 start 参数设置,起始值默认为 1names 参数也可以传入迭代器或映射。

'''以空格分隔'''
TestA = enum.Enum('TestEnum', 'A B C D')
print(list(TestA))
# [<TestEnum.A: 1>, <TestEnum.B: 2>, <TestEnum.C: 3>, <TestEnum.D: 4>]

'''以空格分隔, 设置起始值为10'''
TestB = enum.Enum('TestEnum', 'A B C D', start=10)
print(list(TestB))
# [<TestEnum.A: 10>, <TestEnum.B: 11>, <TestEnum.C: 12>, <TestEnum.D: 13>]

'''传入列表设置枚举成员, 枚举成员的值按顺序自增, 起始值由 start 参数设置'''
TestC = enum.Enum('TestEnum', ['A', 'B', 'C', 'D'])
print(list(TestC))
# [<TestEnum.A: 1>, <TestEnum.B: 2>, <TestEnum.C: 3>, <TestEnum.D: 4>]

'''传入列表, 并且元素格式为 ('名称', '值')'''
TestD = enum.Enum('TestEnum', [('A', 11), ('B', 22), ('C', 33), ('D', 44)])
print(list(TestD))
# [<TestEnum.A: 11>, <TestEnum.B: 22>, <TestEnum.C: 33>, <TestEnum.D: 44>]

'''传入映射, 格式为 {名称: 值}'''
TestE = enum.Enum('TestEnum', {'A': 11, 'B': 22, 'C': 33, 'D': 44})
print(list(TestE))
# [<TestEnum.A: 11>, <TestEnum.B: 22>, <TestEnum.C: 33>, <TestEnum.D: 44>]

module 参数需要传入一个模块,为了在进行封存操作时成功执行。若指定一个错误的模块,那么封存操作将报错。若参数值若设置为 None__name__ 最终指向的模块均为当前文件 __main__

'''module 参数默认为 None'''
TestA = enum.Enum('TestA', 'AAA BBB CCC')
print(pickle.loads(pickle.dumps(TestA)))  # 封存+解封
# <enum 'TestA'>

'''module 参数设置为 __name__'''
TestB = enum.Enum('TestB', 'AAA BBB CCC', module=__name__)
print(pickle.loads(pickle.dumps(TestB)))
# <enum 'TestB'>

'''module 参数设置为一个不存在的模块'''
TestD = enum.Enum('TestD', 'AAA BBB CCC', module='aaa')
print(pickle.loads(pickle.dumps(TestD)))
# _pickle.PicklingError: Can't pickle <enum 'TestD'>: import of module 'aaa' failed

qualname 参数是指明在模块中的具体位置,同 module 参数类似,设置这个参数的目的也是为了封存操作,pickle 协议版本 4 在某些情况下需要依赖设置的具体位置。

布尔值

枚举类的布尔值总是 True,枚举中的枚举成员的布尔值也总是 True。 枚举成员的布尔值与枚举成员的值无关,如果想要以枚举成员的值来计算布尔值,可以修改 __bool__() 魔法方法。

'''不修改 __bool__() 魔法方法的枚举类'''
class Test(enum.Enum):
    A = 0
    B = None

print(bool(Test))
# True
print(bool(Test.A))
# True
print(bool(Test.B))
# True

'''修改 __bool__() 魔法方法的枚举类'''
class Test(enum.Enum):
    A = 0

print(bool(Test.A))
# False

支持枚举类使用的属性

__members__ 是一个 member_name:member 条目的只读有序映射。得到的结果是以 {名称: 枚举成员对象} 为结构的字典。

class Test(enum.Enum):
    A = 1
    B = 2
    C = 1

print(Test.__members__)
# {'A': <Test.A: 1>, 'B': <Test.B: 2>, 'C': <Test.A: 1>}

支持枚举成员使用的属性

  • _name__value_ 分别返回枚举成员的名称和值;
  • _generate_next_value_ 在功能性API中被使用,为枚举成员获取适当的值,也可被打印;
  • _missing_作用是当未发现某个值时所使用的查找函数;
  • _ignore_ 是一个名称列表,结果可以是 list,也可以是 str,不会转化为枚举成员,并将从最终类中被移除;
  • _order_Python2 的遗留属性,在 Python2 中负责记录枚举成员的定义顺序;
'''可直接打印的属性'''
class Test(enum.Enum):
    A = 1
    B = 2
    C = 1

print(Test.A._name_)
# 'A'
print(Test.A._value_)
# 1
print(Test.A._generate_next_value_)
# <bound method Enum._generate_next_value_ of <Test.A: 1>>

'''_ignore_的用法'''
class Period(enum.Enum):
    _ignore_ = 'Period i'
    Period = vars()
    for i in range(3):
        Period['obj_%d' % i] = i

print(list(Period))
# [<Period.obj_0: 0>, <Period.obj_1: 1>, <Period.obj_2: 2>]

公众号 : 「python杂货铺」,专注于 python 语言及其相关知识。发掘更多原创文章,期待您的关注。

参考资料

官方文档: docs.python.org/zh-cn/3/lib…

源代码: Lib/enum.py