python:枚举类
1 前言
python实际开发中,需要定义类型时,一般是使用一组整数或者字符串来表示。如果使用常规的定义方式,非常容易被外部修改,Python3.4后,增加了枚举类的使用,就是为了解决这种场景而设计的。
python枚举适用场景:值有限且固定(不希望被轻易或随意修改)的数据类型。Java也有枚举类的使用,使用关键字enum实现,而python一般是通过提供的Enum类继承实现的(python类支持多继承,java为类单继承)。
2 使用
2.0 初识枚举类
通过enum.Enum()来实现自定义的枚举类:
(‘SPRING’, ‘SUMMER’, ‘AUTUMN’, ‘WINTER’)是定义的多个枚举值:
import enum
# 定义Season枚举类
Season = enum.Enum('Season', ('SPRING', 'SUMMER', 'AUTUMN', 'WINTER'))
# 直接访问指定枚举
print(Season.SPRING)
# 访问枚举成员的变量名
print(Season.SPRING.name)
# 访问枚举成员的值
print(Season.SPRING.value)
# 根据枚举变量名访问枚举对象
print(Season['WINTER']) # Season.WINTER
# 根据枚举值访问枚举对象
print(Season(2)) # Season.SUMMER
# 遍历Season枚举的所有成员
for name, member in Season.__members__.items():
print(name, '=>', member, ',', member.value)
执行结果:
2.1 枚举类简单示例:
还可以通过继承Enum枚举类,来实现一个自定义的枚举类:
from enum import Enum
class OrderStatus(Enum):
NO_PAY = 0
PAID = 1
REFUND = 2
class Type(Enum):
TYPE_ONE = "fruit"
TYPE_TWO = "animal"
TYPE_THREE = "drinks"
print(OrderStatus.REFUND)
# OrderStatus.REFUND
print(Type.TYPE_ONE)
# Type.TYPE_ONE
相比于字典,普通类中的变量,枚举类优势如下:
(1)字典,普通类中的变量支持修改,而枚举类,在类外,是不支持修改的。
OrderStatus.REFUND = 3
# error: AttributeError: Cannot reassign members.
上述报错是因为,我们继承了Enum类,而Enum类的魔法方法def __setattr__(cls, name, value),限制了只要从_member_map_中获取的值,即会抛出异常:
def __setattr__(cls, name, value):
"""
Block attempts to reassign Enum members.
A simple assignment to the class namespace only changes one of the
several possible ways to get an Enum member from the Enum class,
resulting in an inconsistent Enumeration.
"""
member_map = cls.__dict__.get('_member_map_', {
})
if name in member_map:
raise AttributeError('Cannot reassign members.')
super().__setattr__(name, value)
(2)python枚举类中不可以存在key相同的枚举项
下面枚举类有相同key:NO_PAY,会直接抛出异常:
class OrderStatus(Enum):
NO_PAY = 0
PAID = 1
REFUND = 2
NO_PAY = 4
# TypeError: Attempted to reuse key: 'NO_PAY'
由枚举类的魔法方法def __setitem__(self, key, value)控制:
def __setitem__(self, key, value):
"""
Changes anything not dundered or not a descriptor.
If an enum member name is used twice, an error is raised; duplicate
values are not checked for.
Single underscore (sunder) names are reserved.
"""
if _is_private(self._cls_name, key):
import warnings
warnings.warn(
"private variables, such as %r, will be normal attributes in 3.10"
% (key, ),
DeprecationWarning,
stacklevel=2,
)
if _is_sunder(key):
if key not in (
'_order_', '_create_pseudo_member_',
'_generate_next_value_', '_missing_', '_ignore_',
):
raise ValueError('_names_ are reserved for future Enum use')
if key == '_generate_next_value_':
# check if members already defined as auto()
if self._auto_called:
raise TypeError("_generate_next_value_ must be defined before members")
setattr(self, '_generate_next_value', value)
elif key == '_ignore_':
if isinstance(value, str):
value = value.replace(',',' ').split()
else:
value = list(value)
self._ignore = value
already = set(value) & set(self._member_names