Python 系列学习十四:面向对象编程 - 枚举类
前言
打算写一系列文章来记录自己学习 Python 3 的点滴;本章主要介绍 Python 面向对象编程中有关枚举类型的相关内容;
本文为作者的原创作品,转载需注明出处;
Enum
Python 通过内置 class Enum,来实现 Python 中的枚举特性;构造枚举类有两种方式,一种是直接使用 Enum 构造函数的方式,一种是使用继承 Enum 自定义枚举类的方式;下面,我们以一年的十二个月份为例子,来看看相关的枚举类型是如何在 Python 中所定义的;
使用 Enum 构造函数
简单的源码分析
先来大致看看 Enum 的源码是如何实现的;Enum 在模块 enum.py 中,先来看看 Enum 类的片段
|
1
2
3
4
5
6
|
class Enum(metaclass=EnumMeta):
"""Generic enumeration.
Derive from this class to define new enumerations.
"""
|
可以看到,Enum 是继承元类 EnumMeta 的;再看看 EnumMeta 的相关片段
|
1
2
3
4
5
6
7
8
9
10
11
|
class EnumMeta(type):
"""Metaclass for Enum"""
def __members__(cls):
"""Returns a mapping of member name->value.
This mapping lists all enum members, including aliases. Note that this
is a read-only view of the internal mapping.
"""
return MappingProxyType(cls._member_map_)
|
首先__members__方法返回的是一个包含一个Dict既 Map 的 MappingProxyType,并且通过 @property 将方法__members__(cls)的访问方式改变为了变量的的形式,既可以直接通过__members__来进行访问了;
构造一个 Enum
如何构造
|
1
2
|
|
通过上述的两行代码,我们就创建了一个有关月份的枚举类型Month,这里要注意的是构造参数,第一个参数Month表示的是该枚举类的类名,第二个 tuple 参数,表示的是枚举类的值;那么,看看我们如何对其取值以及背后的逻辑;
如何取值
-
遍历所有值
123456789101112131415>>> for key, member in M.__members__.items():... print( key, '=>', member, ',', member.value, ',', type(member) )...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对象;12345678Month.JanMonth.Jan1得到的是一个
Enum类型的对象12<enum 'Month'> -
根据 Enum.value 进行取值,取得是一个
Enum对象123<Month.Dec: 12>取值的时候,注意不能越界,否则报错;
12345678910Traceback (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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
from enum import Enum, unique
class Month(Enum):
Jan = 1
Feb = 2
Mar = 3
Apr = 4
May = 5
Jun = 6
Jul = 7
Aug = 8
Sep = 9
Oct = 10
Nov = 11
Dec = 12
|
是的,自定义构造一个枚举类就是那么简单,看看其调用逻辑,循环取得所有值,
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
>>> for name, member in Month.__members__.items():
... print(name, '=>', member, ',', member.value)
...
Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12
|

浙公网安备 33010602011771号