学习环境: windows系统,python3.7,PyCharm
enum库学习笔记——枚举法
枚举是一组有唯一常量值的符号名称(成员)的集合。在枚举中,成员可以通过标识进行比较,并且枚举本身可以遍历。
主要内容
本模块提供了四种枚举类Enum,IntEnum,Flag和IntFlag,以及一个装饰器unique()和一个helper auto。
四种枚举类
enum.Enum | 用于创建枚举常量的基类。 |
---|---|
enum.IntEnum | 用于创建枚举常量的基类,这些枚举常量也是int的子类。 |
enum.IntFlag | 用于创建枚举常量的基类,这些常量可以使用按位操作符组合,而不会丢失它们的IntFlag成员关系。IntFlag成员也是int的子类。 |
enum.Flag | 用于创建枚举常量的基类,这些枚举常量可以使用按位操作组合而不丢失其标志成员关系。 |
- enum.unique():Enum类装饰器,确保只有一个名称绑定到任何一个值。
- class enum.auto:把实例的Enum成员替换为适当值。初始值从1开始。
创建一个Enum
枚举需要用class语句创建
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
Note:Enum的成员值可以是任意类型:int,str,等等。如果并不需要一个准确值,那就可以使用auto方法,自动生成一个合适的值。
Note:命名法。
类Color就是一个枚举(enum),其中的Color.RED, Color.GREEN等属性都是枚举成员,并具有功能性常量。enum成员有names和values,Color.RED的name是RED,value是1。
查看成员
from enum import Enum
class Color(Enum):
RED = 1
Green = 2
BLUE = 3
print(Color.RED)
print(Color.RED.name)
print(Color.RED.value)
for color in Color:
print(color)
Color.RED
RED
1
Color.RED
Color.Green
Color.BLUE
枚举成员都是散列的,所以可以用于字典和集合中
apples = dict()
apples[Color.RED] = 'red delicious'
apples[Color.GREEN] = 'granny smith'
print(apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'})
print(apples)
对枚举成员及其属性的编程访问
当不知道枚举成员及其属性的具体值时
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color(1))
print(Color(2))
print(Color(3))
当知道枚举成员的属性时
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color['RED'])
print(Color['GREEN'])
print(Color['BLUE'])
当知道enum成员,要访问成员name和value时
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
member = Color.RED
print(member.name)
print(member.value)
复制enum成员及值
enum不允许两个成员的name相同,但是却允许两个不同name的成员有相同的value。
当A和B都有相同的value时,并且先定义了A,意味着B是A的别名,所有访问B都会返回到A。
from enum import Enum
class Shape(Enum):
SQUARE = 2
DIAMOND = 1
CIRCLE = 3
ALIAS_FOR_SQUARE = 2
print(Shape.SQUARE)
print(Shape.ALIAS_FOR_SQUARE)
print(Shape(2))
确保枚举值唯一
enum默认可以为一个value创建多个别名。当这种行为不被允许时,就可以使用下面的装饰器来确保枚举值唯一。
@enum.unique
这个装饰器可以用来检测别名的存在,当检测到别名时会抛出ValueError异常。
from enum import Enum, unique
@unique
class Mistake(Enum):
ONE = 1
TWO = 2
THREE = 3
FOUR = 3
结果:
Traceback (most recent call last):
File "C:/Users/abc/PycharmProjects/WD/xxxxxxx.py", line 3, in <module>
class Mistake(Enum):
File "C:\ProgramData\Anaconda3\envs\WD\lib\enum.py", line 869, in unique
(enumeration, alias_details))
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE
使用自定义值
如果并不需要指定准确值,可以使用auto:
from enum import Enum, auto
class Color(Enum):
RED = auto()
BLUE = auto()
GREEN = auto()
print(list(Color))
自定义值是由_generate_next_value_()方法决定的,并且可以被覆写
from enum import Enum, auto
class AutoName(Enum):
def _generate_next_value_(name, start, count, last_values):
return name
class Ordinal(AutoName):
NORTH = auto()
SOUTH = auto()
EAST = auto()
WEST = auto()
print(list(Ordinal))
迭代
迭代去除别名后的enum
from enum import Enum
class Shape(Enum):
SQUARE = 2
DIAMOND = 1
CIRCLE = 3
ALIAS_FOR_SQUARE = 2
print(list(Shape))
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
而__members__方法可以通过name访问到enum的成员,包括别名成员
from enum import Enum
class Shape(Enum):
SQUARE = 2
DIAMOND = 1
CIRCLE = 3
ALIAS_FOR_SQUARE = 2
for name, member in Shape.__members__.items():
print(name, member)
SQUARE Shape.SQUARE
DIAMOND Shape.DIAMOND
CIRCLE Shape.CIRCLE
ALIAS_FOR_SQUARE Shape.SQUARE
__member__的特殊使用方法: 查找别名
from enum import Enum
class Shape(Enum):
SQUARE = 2
DIAMOND = 1
CIRCLE = 3
ALIAS_FOR_SQUARE = 2
for name, member in Shape.__members__.items():
if member.name != name:
print(name)
ALIAS_FOR_SQUARE
成员比较
枚举成员可用过ID比较
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color.RED is Color.RED)
print(Color.RED is Color.BLUE)
print(Color.RED is not Color.BLUE)
另外还可以使用==,!=比较,不能使用<或>比较符;而且枚举成员和任何非枚举类型相比较,结果都为False。
枚举成员和属性的范围
在前面的例子中均使用了整数值。在绝大多数用例中,人们并不关心枚举的实际值是什么。
但如果值很重要,枚举可以具有任意值。
枚举是Python类,可以有以下的使用方法:
from enum import Enum
class Mood(Enum):
FUNKY = 1
HAPPY = 3
def describe(self):
# self is the member here
return self.name, self.value
def __str__(self):
return 'my custom str! {0}'.format(self.value)
@classmethod
def favorite_mood(cls):
# cls here is the enumeration
return cls.HAPPY
print(Mood.favorite_mood())
print(Mood.HAPPY.describe())
print(str(Mood.FUNKY))
Enum子类化的限制
一个新的Enum类必须有一个Enum基类,最多一个具体的数据类型以及必需的基于对象的mixin类,如:
class EnumName([mix-in, ...,] [data-type,] base-enum):
pass
只有当一个枚举没有定义任何成员时才可以创建子类,或如下。
class Foo(Enum):
def some_behavior(self):
pass
class Bar(Foo):
HAPPY = 1
SAD = 2
衍生枚举
IntEnum
该类的成员可以与整数值相比较,而且它们之间也可以进行比较。
from enum import IntEnum
class Shape(IntEnum):
CIRCLE = 1
SQUARE = 2
class Request(IntEnum):
POST = 1
GET = 2
print(Shape == 1)
print(Shape.CIRCLE == 1)
print(Shape.CIRCLE == Request.POST)
但是,IntEnum类成员仍无法和标准Enum枚举进行比较:
from enum import IntEnum, Enum
class Shape(IntEnum):
CIRCLE = 1
SQUARE = 2
class Color(Enum):
RED = 1
GREEN = 2
print(Shape.CIRCLE == Color.RED)
IntEnum成员的值可以像整数值一样使用
from enum import IntEnum, Enum
class Shape(IntEnum):
CIRCLE = 1
SQUARE = 2
class Color(Enum):
RED = 1
GREEN = 2
print(int(Shape.CIRCLE))
print(['a', 'b', 'c'][Shape.CIRCLE])
print([i for i in range(Shape.SQUARE)])
IntFlag
大部分操作和IntEnum类似,又有所不同。
from enum import IntFlag
class Perm(IntFlag):
R = 4
W = 2
X = 1
RW = Perm.R | Perm.W
print(Perm.R | Perm.W)
print(Perm.R + Perm.W)
print(Perm.R in RW)
from enum import IntFlag
class Perm(IntFlag):
R = 4
W = 2
X = 1
RWX = 7
print(Perm.RWX)
print(~Perm.RWX)
IntFlag和Enum一个重要的不同点在于,如果IntFlag没有指定任何flags(value为0),那么其布尔值将为False。
from enum import IntFlag
class Perm(IntFlag):
R = 4
W = 2
X = 1
RWX = 7
print(Perm.R & Perm.X)
print(bool(Perm.R & Perm.X))
另外IntFlag还可以与整数值结合
from enum import IntFlag
class Perm(IntFlag):
R = 4
W = 2
X = 1
RWX = 7
print(Perm.X | 8)
Flag
该类与IntFlag类基本相同,但是Flag成员之间不能结合,与其他类型数据也不可结合。
和IntFlag一样,当没有flag或value为0时,布尔值为Flase。
from enum import Flag, auto
class Color(Flag):
RED = auto()
BLUE = auto()
GREEN = auto()
print(Color.RED & Color.GREEN)
print(bool(Color.RED & Color.GREEN))
flags的值从1开始,之后每次翻倍,即1,2,4,8。但是成员组合并不是。
from enum import Flag, auto
class Color(Flag):
RED = auto()
BLUE = auto()
GREEN = auto()
WHITE = RED | BLUE | GREEN
print(Color.WHITE.value)
一些实例
应用于忽略value的例子
即列举成员的value不重要时
- 使用auto
from enum import Enum, auto
class NoValue(Enum):
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
class Color(NoValue):
RED = auto()
BLUE = auto()
GREEN = auto()
print(Color.GREEN.value)
- 使用object
from enum import Enum, auto
class NoValue(Enum):
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
class Color(NoValue):
RED = object()
GREEN = object()
BLUE = object()
print(Color.GREEN.value)
- 使用描述性字符串
from enum import Enum, auto
class NoValue(Enum):
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
class Color(NoValue):
RED = 'stop'
GREEN = 'go'
BLUE = 'too fast!'
print(Color.GREEN.value)
- 使用__new__()
from enum import Enum
class NoValue(Enum):
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
class AutoNumber(NoValue):
def __new__(cls):
value = len(cls.__members__) + 1
obj = object.__new__(cls)
obj._value_ = value
return obj
class Color(AutoNumber):
RED = ()
GREEN = ()
BLUE = ()
print(Color.GREEN.value)
有序的Enum
from enum import Enum
class OrderedEnum(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)
查重Enum
当检查出一个重复成员name时,抛出一个错误,而不是创建一个别名。
from enum import Enum
class DuplicateFreeEnum(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
Planet
from enum import Enum
class Planet(Enum):
MERCURY = (3.303e+23, 2.4397e6)
VENUS = (4.869e+24, 6.0518e6)
EARTH = (5.976e+24, 6.37814e6)
MARS = (6.421e+23, 3.3972e6)
JUPITER = (1.9e+27, 7.1492e7)
SATURN = (5.688e+26, 6.0268e7)
URANUS = (8.686e+25, 2.5559e7)
NEPTUNE = (1.024e+26, 2.4746e7)
def __init__(self, mass, radius):
self.mass = mass # in kilograms
self.radius = radius # in meters
@property
def surface_gravity(self):
# universal gravitational constant (m3 kg-1 s-2)
G = 6.67300E-11
return G * self.mass / (self.radius * self.radius)
print(Planet.EARTH.value)
print(Planet.EARTH.surface_gravity)
TimePeriod
一个使用_ignore_属性的例子
from enum import Enum
from datetime import timedelta
class Period(timedelta, Enum):
"different lengths of time"
_ignore_ = 'Period i'
Period = vars()
for i in range(367):
Period['day_%d' % i] = i
print(list(Period)[:2])
print(list(Period)[-2:])