前言
打算写一系列文章来记录自己学习 Python 3 的点滴;本章主要介绍 Python 面向对象编程中有关枚举类型的相关内容;
本文为作者的原创作品,转载需注明出处;
Enum
Python 通过内置 class Enum
,来实现 Python 中的枚举特性;构造枚举类有两种方式,一种是直接使用 Enum 构造函数的方式,一种是使用继承 Enum 自定义枚举类的方式;下面,我们以一年的十二个月份为例子,来看看相关的枚举类型是如何在 Python 中所定义的;
使用 Enum 构造函数
简单的源码分析
先来大致看看 Enum 的源码是如何实现的;Enum 在模块 enum.py 中,先来看看 Enum 类的片段
1 | class Enum(metaclass=EnumMeta): |
可以看到,Enum 是继承元类 EnumMeta 的;再看看 EnumMeta 的相关片段
1 | class EnumMeta(type): |
首先__members__
方法返回的是一个包含一个Dict
既 Map 的 MappingProxyType,并且通过 @property 将方法__members__(cls)
的访问方式改变为了变量的的形式,既可以直接通过__members__
来进行访问了;
构造一个 Enum
如何构造
1 | from enum import Enum |
通过上述的两行代码,我们就创建了一个有关月份的枚举类型Month
,这里要注意的是构造参数,第一个参数Month
表示的是该枚举类的类名,第二个 tuple 参数,表示的是枚举类的值;那么,看看我们如何对其取值以及背后的逻辑;
如何取值
遍历所有值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15for key, member in M.__members__.items():
'=>', member, ',', member.value, ',', type(member) ) print( key,
Jan => Month.Jan , 1 , <enum 'Month'>
Feb => Month.Feb , 2 , <enum 'Month'>
Mar => Month.Mar , 3 , <enum 'Month'>
Apr => Month.Apr , 4 , <enum 'Month'>
May => Month.May , 5 , <enum 'Month'>
Jun => Month.Jun , 6 , <enum 'Month'>
Jul => Month.Jul , 7 , <enum 'Month'>
Aug => Month.Aug , 8 , <enum 'Month'>
Sep => Month.Sep , 9 , <enum 'Month'>
Oct => Month.Oct , 10 , <enum 'Month'>
Nov => Month.Nov , 11 , <enum 'Month'>
Dec => Month.Dec , 12 , <enum 'Month'>首先,M.__members__.items() 中所包含的每个元素 item 是什么?
我们来分析一下上述的代码,正如简单的源码分析中我们知道
Month.__members__
返回的是一个包含Dict
的 MappingProxyType 对象,通过调用其 items() 方法既可以返回该Dict
,从上述执行的结果中,我们不难分析出该Dict
的组成结构是 <key, value=member> 这样的结构,key 就是对应的月份的名称,value 对应的是一个member
对象,可以通过 member.value 获取 int 排序值;所以,综上所述,一个 item 既是上述Dict
的一个键值对,键是月份的名称,值是一个member
对象;再次,那么
member
又是指的是什么呢?通过上述的输出结果,我们不难发现,
member
就是一个Enum
对象;取某个固定值
根据 Key 进行取值,取得的是一个
Enum
对象;1
2
3
4
5
6
7
8Jan = M.Jan
print(Jan)
Month.Jan
'Jan'] Jan = M[
print(Jan)
Month.Jan
print(Jan.value)
1得到的是一个
Enum
类型的对象1
2type(Jan)
<enum 'Month'>根据 Enum.value 进行取值,取得是一个
Enum
对象1
2
312) Feb = Month(
Feb
<Month.Dec: 12>取值的时候,注意不能越界,否则报错;
1
2
3
4
5
6
7
8
9
1013) Month(
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/enum.py", line 291, in __call__
return cls.__new__(cls, value)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/enum.py", line 533, in __new__
return cls._missing_(value)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/enum.py", line 546, in _missing_
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 13 is not a valid Month
继承 Enum 自定义枚举类
那么,除了使用内置的 Enum 对象来构造枚举类型以外,我们该如何自定义自己的枚举类呢?同样以月份为例子,这次我们来构建自己的枚举类型,
1 | from enum import Enum, unique |
是的,自定义构造一个枚举类就是那么简单,看看其调用逻辑,循环取得所有值,
1 | for name, member in Month.__members__.items(): |