enum库学习笔记——枚举

学习环境: 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不重要时

  1. 使用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)
  1. 使用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)
  1. 使用描述性字符串
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)
  1. 使用__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:])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ALittleHigh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值