Python enum的使用总结

枚举(enumeration)在许多编程语言中常被表示为一种基础的数据结构使用,枚举帮助组织一系列密切相关的成员到同一个群组机制下,一般各种离散的属性都可以用枚举的数据结构定义,比如颜色、季节、国家、时间单位等
在Python中没有内置的枚举方法,起初模仿实现枚举属性的方式是

class Directions:NORTH = 1EAST = 2SOUTH = 3WEST = 4

使用成员

Direction.EAST
Direction.SOUTH

检查成员

>>> print("North的类型:", type(Direction.NORTH))
>>> print(isinstance(Direction.EAST, Direction))
North的类型: <class 'int'>
False

成员NORTH的类型是int,而不是Direction,这个做法只是简单地将属性定义到类中

Python标准库enum实现了枚举属性的功能,接下来介绍enum的在实际工作生产中的用法

为什么要用enum,什么时候使用enum?

enum规定了一个有限集合的属性,限定只能使用集合内的值,明确地声明了哪些值是合法值,,如果输入不合法的值会引发错误,只要是想要从一个限定集合取值使用的方式就可以使用enum来组织值。

enum的定义/声明

from enum import Enumclass Directions(Enum):NORTH = 1EAST = 2SOUTH = 3WEST = 4

使用和类型检查:

>>> Directions.EAST
<Directions.EAST: 2>
>>> Directions.SOUTH
<Directions.SOUTH: 3>
>>> Directions.EAST.name
'EAST'
>>> Directions.EAST.value
2
>>> print("South的类型:", type(Directions.SOUTH))
South的类型: <enum 'Directions'>
>>> print(isinstance(Directions.EAST, Directions))
True
>>>

检查示例South的的类型,结果如期望的是Directions。name和value是两个有用的附加属性。

实际工作中可能会这样使用

fetched_value = 2  # 获取值
if Directions(fetched_value) is Directions.NORTH:...
elif Directions(fetched_value) is Directions.EAST:...
else:...

输入未定义的值时:

>>> Directions(5)
ValueError: 5 is not a valid Directions

遍历成员

>>> for name, value in Directions.__members__.items():
...     print(name, value)
...
NORTH Directions.NORTH
EAST Directions.EAST
SOUTH Directions.SOUTH
WEST Directions.WEST

在继承Enum的类中定义方法

可以用于将定义的值转换为获取需要的值

from enum import Enumclass Directions(Enum):NORTH = 1EAST = 2SOUTH = 3WEST = 4def angle(self):right_angle = 90.0return right_angle * (self.value - 1)@staticmethoddef angle_interval(direction0, direction1):return abs(direction0.angle() - direction1.angle())
>>> east = Directions.EAST
>>> print("SOUTH Angle:", east.angle())
SOUTH Angle: 90.0
>>> west = Directions.WEST
>>> print("Angle Interval:", Directions.angle_interval(east, west))
Angle Interval: 180.0

将Enum类属性的值定义为函数或方法

from enum import Enum
from functools import partialdef plus_90(value):return Directions(value).angle + 90class Directions(Enum):NORTH = 1EAST = 2SOUTH = 3WEST = 4PLUS_90 = partial(plus_90)def __call__(self, *args, **kwargs):return self.value(*args, **kwargs)@propertydef angle(self):right_angle = 90.0return right_angle * (self.value - 1)print(Directions.NORTH.angle)
print(Directions.EAST.angle)
south = Directions(3)
print("SOUTH angle:", south.angle)
print("SOUTH angle plus 90: ", Directions.PLUS_90(south.value))

输出:

0.0
90.0
SOUTH angle: 180.0
SOUTH angle plus 90:  270.0

key: 1.将函数方法用partial包起来;2.定义__call__方法。

忽略大小写

class TimeUnit(Enum):MONTH = "MONTH"WEEK = "WEEK"DAY = "DAY"HOUR = "HOUR"MINUTE = "MINUTE"@classmethoddef _missing_(cls, value: str):for member in cls:if member.value == value.upper():return member
print(TimeUnit("MONTH"))
print(TimeUnit("Month"))

继承父类Enum的_missing_方法,在值的比较时将case改为一致即可
输出

TimeUnit.MONTH
TimeUnit.MONTH

自定义异常处理

第一种,执行SomeEnum(“abc”)时想要引发自定义错误,其中"abc"是未定义的属性值

class TimeUnit(Enum):MONTH = "MONTH"WEEK = "WEEK"DAY = "DAY"HOUR = "HOUR"MINUTE = "MINUTE"@classmethoddef _missing_(cls, value: str):raise Exception("Customized exception")print(TimeUnit("MONTH"))
TimeUnit("abc")

输出

TimeUnit.MONTHValueError: 'abc' is not a valid TimeUnit
...
Exception: Customized exception

第二种,执行SomeEnum.__getattr__(“ABC”)时,想要引发自定义错误,其中"ABC"是未定义的属性名称,需要重写一下EnumMeta中的__getattr__方法,然后指定实例Enum对象的的metaclass

from enum import Enum, EnumMeta
from functools import partialclass SomeEnumMeta(EnumMeta):def __getattr__(cls, name: str):value = cls.__members__.get(name.upper())   # (这里name是属性名称,可以自定义固定传入大写(或小写),对应下面的A1是大写)if not value:raise Exception("Customized exception")return valueclass SomeEnum1(Enum, metaclass=SomeEnumMeta):A1 = "123"class SomeEnum2(Enum, metaclass=SomeEnumMeta):A1 = partial(lambda x: x)def __call__(self, *args, **kwargs):return self.value(*args, **kwargs)print(SomeEnum1.__getattr__("A1"))
print(SomeEnum2.__getattr__("a1")("123"))
print(SomeEnum2.__getattr__("B")("123"))

输出

SomeEnum1.A1123...
Exception: Customized exception

enum的进阶用法

Functional APIs
动态创建和修改Enum对象,可以在不修改原定义好的Enum类的情况下,追加修改,这里借用一个说明示例,具体的场景使用案例可以看下面的场景举例

>>> # Create an Enum class using the functional API
... DirectionFunctional = Enum("DirectionFunctional", "NORTH EAST SOUTH WEST", module=__name__)
... # Check what the Enum class is
... print(DirectionFunctional)
... # Check the items
... print(list(DirectionFunctional))
... print(DirectionFunctional.__members__.items())
...
<enum 'DirectionFunctional'>
[<DirectionFunctional.NORTH: 1>, <DirectionFunctional.EAST: 2>, <DirectionFunctional.SOUTH: 3>, <DirectionFunctional.WEST: 4>]
dict_items([('NORTH', <DirectionFunctional.NORTH: 1>), ('EAST', <DirectionFunctional.EAST: 2>), ('SOUTH', <DirectionFunctional.SOUTH: 3>), ('WEST', <DirectionFunctional.WEST: 4>)])
>>> # Create a function and patch it to the DirectionFunctional class
... def angle(DirectionFunctional):
...     right_angle = 90.0
...     return right_angle * (DirectionFunctional.value - 1)
...
...
... DirectionFunctional.angle = angle
...
... # Create a member and access its angle
... south = DirectionFunctional.SOUTH
... print("South Angle:", south.angle())
...
South Angle: 180.0

注:这里没有使用类直接声明的方式来执行枚举(定义时如果不指定值默认是从1开始的数字,也就相当于NORTH = auto(),auto是enum中的方法),仍然可以在后面为这个动态创建的DirectionFunctional创建方法,这种在运行的过程中修改对象的方法也就是python的monkey patching。

Functional APIs的用处和使用场景举例:

在不修改某定义好的Enum类的代码块的情况下,下面示例中是Arithmethic类,可以认为是某源码库我们不想修改它,然后增加这个Enum类的属性,有两种方法:
1.enum.Enum对象的属性不可以直接被修改,但我们可以动态创建一个新的Enum类,以拓展原来的Enum对象
例如要为下面的Enum对象Arithmetic增加一个取模成员MOD="%",但是又不能修改Arithmetic类中的代码块:

# enum_test.py
from enum import Enumclass Arithmetic(Enum):ADD = "+"SUB = "-"MUL = "*"DIV = "/"

就可以使用enum的Functional APIs方法:

# functional_api_test.py
from enum import EnumDynamicEnum = Enum("Arithmetic", {"MOD": "%"}, module="enum_test", qualname="enum_test.Arithmetic")print(DynamicEnum.MOD)print(eval(f"5 {DynamicEnum.MOD.value} 3"))

输出:

Arithmetic.MOD
2

注意:动态创建Enum对象时,要指定原Enum类所在的module名称: "Yourmodule",否则执行时可能会因为找不到源无法解析,qualname要指定类的位置:"Yourmodule.YourEnum",值用字符串类型

2.使用aenum.extend_enum可以动态修改enum.Enum对象
为enum.Enum类Arithmetic增加一个指数成员EXP="**",且不修改原来的Arithmetic类的代码块:

# functional_api_test.py
from aenum import extend_enum
from enum_test import Arithmeticextend_enum(Arithmetic, "EXP", "**")
print(Arithmetic, list(Arithmetic))
print(eval(f"2 {Arithmetic.EXP.value} 3"))

输出

<enum 'Arithmetic'> [<Arithmetic.ADD: '+'>, <Arithmetic.SUB: '-'>, <Arithmetic.MUL: '*'>, <Arithmetic.DIV: '/'>, <Arithmetic.EXP: '**'>]
8

参考:https://betterprogramming.pub/take-advantage-of-the-enum-class-to-implement-enumerations-in-python-1b65b530e1d
https://docs.python.org/3/library/enum.html
https://stackoverflow.com/questions/28126314/adding-members-to-python-enums

Python enum的使用总结相关推荐

  1. boost::python::enum_相关的测试程序

    boost::python::enum_相关的测试程序 实现功能 C++实现代码 实现功能 boost::python::enum_相关的测试程序 C++实现代码 #include <boost ...

  2. python里load什么意思_【python】json中load和loads区别

    相同点 dump 和 dumps 都实现了序列化 load 和 loads 都实现反序列化 变量从内存中变成可存储或传输的过程称之为序列化 序列化是将对象状态转化为可保存或可传输格式的过程. 变量内容 ...

  3. repo:.repo/manifest/default.xml详解

    <?xml version="1.0" encoding="UTF-8"?> <manifest> <! -remote 标签指定 ...

  4. 【Android】Android源码版本

    学而不思则罔,思而不学则殆 [Android]Android源码版本 1.下载manifest.git 2.checkout到不同的分支 3.源码下载地址列表 4.Android源码存在的分支 1.下 ...

  5. 2021-10-22 学习笔记:FastAPI基础使用指南

    2021-10-22 学习笔记:FastAPI基础使用指南 已经第三针疫苗了,祝所有人平安! 简单使用 路径参数 查询参数 请求体 查询参数和字符串校验 路径参数和数值校验 请求体参数 请求体 -- ...

  6. python3.7如何使用enum_我如何用Python表示‘Enum’?

    在PEP 435之前,Python没有类似的功能,但是您可以实现自己的. 我自己,我喜欢保持简单(我在网上看到了一些非常复杂的例子),像这样的.class Animal: DOG = 1 CAT = ...

  7. python之枚举类Enum

    常量是任何一门语言中都会使用的一种变量类型 如 要表示星期常量,我们可能会直接定义一组变量 JAN = 1 TWO = 2 ... 然后在返回给前端的时候,我们返回的就会是1,2,...这种魔法数字, ...

  8. python并且怎么表示_Python-如何在Python中表示“Enum”?

    小编典典 如PEP 435中所述,将枚举添加到Python 3.4中.它也已在pypi上反向移植到 3.3.3.2.3.1.2.7.2.6.2.5 和2.4. 对于更高级的Enum技术,请尝试aenu ...

  9. Python 进阶:enum 模块源码分析

    作者:weapon 来源:https://zhuanlan.zhihu.com/p/52056538 起步 上一篇<Python 的枚举类型> (https://zhuanlan.zhih ...

最新文章

  1. acwing算法题--混合背包问题
  2. ifstream eof()函数
  3. java编程赋值运算符_(24)赋值运算符
  4. python 批量创建变量_Python批量生成和调用变量
  5. php分页代码 页数太多,php分页函数示例代码分享
  6. cover-letter
  7. numpy.mean() in Python
  8. python中的常量_深入理解Python中的内置常量
  9. 计算机的进制的转换公式,计算机进制转换公式
  10. 【CS106】编译原理笔记4 —— 用普遍性泵引理(Pumping lemma)证明语言的正则性
  11. matlab进行预测误差过大,神经网络预测误差太大怎么办,如何看预测结果
  12. NTFS,exFAT,FAT32最详细的评测
  13. mysql的exception_mysqlexception
  14. 用jQuery实现9宫格抽奖
  15. 常用颜色RGB、灰度值
  16. 用计算机进行情报检测属于计算机应用中,[单选] 计算机用于情报检索属于计算机应用中的()。...
  17. gtbook安装使用教程
  18. FTP服务器vsftpd安装架设
  19. Vue打包文件名、默认文件名操作
  20. 车载传感器技术详解【 持续更新ing 】— 自动驾驶技术概述

热门文章

  1. 如何在windows上将本地项目上传到Github
  2. 力软 java主从表保存_JAVA常用知识总结(十二)——数据库(二)
  3. python设置时间步长与时间离散格式_python怎么定义时间
  4. Cmd Markdown 编辑阅读器
  5. 【机器学习】opencv-数据预处理
  6. MySQL基本指令汇总
  7. Python3.10 结构化模式匹配 PEP 634
  8. Kinect深度图与摄像头RGB的标定与配准(转载文章)
  9. c++学习书籍推荐《超越C++标准库:Boost库导论》下载
  10. SQL查询优化《三》:少做重复的工作