enum模块(枚举类型)
enum模块: python enum模块提供了枚举功能。有些编程语言是内置了枚举类型,但python没有内置,只是用enum模块里的Enum类来实现类似功能。
如果不使用枚举,我们一般会采用定义常量的方式来处理。比如下面代码:
RED, GREEN, YELLOW = range(3)
下面演示了一个常见的enum类的创建:
from enum import Enum
class Day(Enum):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
print(list(Day))
# [<Day.MONDAY: 1>, <Day.TUESDAY: 2>, <Day.WEDNESDAY: 3>, <Day.THURSDAY: 4>, <Day.FRIDAY: 5>, <Day.SATURDAY: 6>, <Day.SUNDAY: 7>]
print(type(Day)) # <class 'enum.EnumType'>
print(type(Day.MONDAY)) # <enum 'Day'>
print(Day.MONDAY) # Day.MONDAY
枚举类型是单例模式,类中的每个成员都是该枚举类的一个实例。枚举类一旦创建,其成员将不可变。包括不能增加或减少,也不能重新赋值。
Day.MONDAY = 0
这样的代码会报错。
还可以像下面这样简单的对其成员赋值,一旦类创建好后,这些类成员就是这个类的实例。
from enum import Enum
class Season(Enum):
WINTER, SPRING, SUMMER, FALL = range(1, 5)
Enum也是类,所以其不光有其枚举型的成员,还可以自定义方法。一个enum可以继承另一个没有成员的enum。 所以,定义没有成员的enum主要是为了定义一些方法。比如下面示例:
from enum import Enum
import string
class BaseTextEnum(Enum):
def as_list(self):
try:
return list(self.value)
except TypeError:
return [str(self.value)]
class Alphabet(BaseTextEnum):
LOWERCASE = string.ascii_lowercase
UPPERCASE = string.ascii_uppercase
Alphabet.LOWERCASE.as_list() # ['a', 'b', 'c', 'd', ..., 'x', 'y', 'z']
还可以使用函数来创建enum,但这一般用于动态编程。 该函数签名如下:
Enum(
value,
names,
*,
module=None,
qualname=None,
type=None,
start=1
)
一般我们只用前两个参数。 第一个是一个字符串,用来代表枚举类型的名称。 第二个names参数可以是一个列表,一个字典等,或一个由二元组构成的列表。用于代表每个成员,即枚举值。使用字典可以同时指定name和value。使用列表则只指定name,value会被自动生成。
from enum import Enum
HTTPMethod = Enum(
"HTTPMethod", ["GET", "POST", "PUSH", "PATCH", "DELETE"]
)
上面使用Enum函数创建的enum枚举类等价于下面这种常规方法:
from enum import Enum
class HTTPMethod(Enum):
GET = 1
POST = 2
PUSH = 3
PATCH = 4
DELETE = 5
auto方法: 用于自动生成枚举成员的value。有时我们并不在乎成员的value。这时可以使用auto方法来自动生成。 同时我们还可以通过定义_generate_next_value_
方法来定制化value的生成逻辑。
from enum import Enum, auto
class CardinalDirection(Enum):
def _generate_next_value_(name, start, count, last_values):
return name[0]
NORTH = auto()
SOUTH = auto()
EAST = auto()
WEST = auto()
print(list(CardinalDirection))
输出结果:
[ <CardinalDirection.NORTH: 'N'>, <CardinalDirection.SOUTH: 'S'>, <CardinalDirection.EAST: 'E'>, <CardinalDirection.WEST: 'W'>]
aliases(别名): 枚举成员的name是不能重复的,但不同的成员却可以有相同的value。 但这种情况下,后出现的成员将被认定为前面成员的别名。 但我们可以使用__members__
方法来访问所有成员,包括别名。 通过id()函数验证成员,发现成员及其别名返回的id是相同的,说明它们就是指向同一个对象。
from enum import Enum
class OperatingSystem(Enum):
UBUNTU = "linux"
MACOS = "darwin"
WINDOWS = "win"
DEBIAN = "linux"
print(id(OperatingSystem.UBUNTU)) # 3088959311888
print(id(OperatingSystem.DEBIAN)) # 3088959311888
print(id(OperatingSystem.MACOS)) # 3088959311952
# Aliases aren't listed
print(list(OperatingSystem))
# [<OperatingSystem.UBUNTU: 'linux'>, <OperatingSystem.MACOS: 'darwin'>, <OperatingSystem.WINDOWS: 'win'>]
# To access aliases, use __members__
print(list(OperatingSystem.__members__.items()))
# [('UBUNTU', <OperatingSystem.UBUNTU: 'linux'>), ('MACOS', <OperatingSystem.MACOS: 'darwin'>),
# ('WINDOWS', <OperatingSystem.WINDOWS: 'win'>), ('DEBIAN', <OperatingSystem.UBUNTU: 'linux'>)]
当我们不想让枚举成员出现重复的value时,可以加unique装饰器。这样声明时就会提醒报错了,比如下面这个示例。
from enum import Enum, unique
@unique
class OperatingSystem(Enum):
UBUNTU = "linux"
MACOS = "darwin"
WINDOWS = "win"
DEBIAN = "linux"
在使用枚举值时,我们有三种访问方法。点访问属性的方式,call方式,以及中括号下标方式。
from enum import Enum
class CardinalDirection(Enum):
NORTH = "N"
SOUTH = "S"
EAST = "E"
WEST = "W"
# Dot notation
CardinalDirection.NORTH
# Call notation
CardinalDirection("N")
# Subscript notation
CardinalDirection["NORTH"]
还可以枚举值的两个属性,name, value。
from enum import Enum
class Semaphore(Enum):
RED = 1
YELLOW = 2
GREEN = 3
Semaphore.RED.name # 'RED'
Semaphore.RED.value # 1
Semaphore.YELLOW.name # 'YELLOW'
枚举类是可迭代的,我们可以使用for loop方法来迭代.
from enum import Enum
class Flavor(Enum):
VANILLA = 1
CHOCOLATE = 2
MINT = 3
for flavor in Flavor:
print(flavor)
输出结果:
Flavor.VANILLA
Flavor.CHOCOLATE
Flavor.MINT
enum类型值之间可以全用is
和==
来比较,但不能使用">"或"<"来比较。可以使用in
来判断是否一个成员是在一个enum类中。注意,两个不同的enum类,即便成员名称和值相同,使用is
和==
来比较时也会是False。
from enum import Enum
class Semaphore(Enum):
RED = 1
YELLOW = 2
GREEN = 3
ALIAS_RED = 1
print(Semaphore.RED in Semaphore) # True
print(Semaphore.ALIAS_RED in Semaphore) # True
print(Semaphore.RED is Semaphore.ALIAS_RED) # True
print(Semaphore.RED == Semaphore.ALIAS_RED) # True
print(Semaphore.RED == 1) # False
# 下面这行会报错:'>' not supported between instances of 'Semaphore' and 'Semaphore'
# print(Semaphore.YELLOW > Semaphore.RED)
也正因为Enum类并未实现>
和<
操作符,所以不能使用sorted()函数。比如下面这个示例就会报错。
from enum import Enum
class Season(Enum):
SPRING = 1
SUMMER = 2
AUTUMN = 3
WINTER = 4
sorted(Season) # TypeError: '<' not supported between instances of 'Season' and 'Season'
但我们可以使用其成员的name或value属性来进行排序。
sorted(Season, key=lambda season: season.value)
sorted(Season, key=lambda season: season.name)
我们可以在定义枚举类型时,增加一些方法,因为枚举类型也遵循正常定义class的方式。
>>> from enum import Enum
>>> class Mood(Enum):
... FUNKY = 1
... MAD = 2
... HAPPY = 3
...
... def describe_mood(self):
... return self.name, self.value
...
... def __str__(self):
... return f"I feel {self.name}"
...
... @classmethod
... def favorite_mood(cls):
... return cls.HAPPY
...
>>> Mood.HAPPY.describe_mood()
('HAPPY', 3)
>>> print(Mood.HAPPY)
I feel HAPPY
>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
再举一个示例:
>>> from enum import Enum
>>> class Sort(Enum):
... ASCENDING = 1
... DESCENDING = 2
... def __call__(self, values):
... return sorted(values, reverse=self is Sort.DESCENDING)
...
>>> numbers = [5, 2, 7, 6, 3, 9, 8, 4]
>>> Sort.ASCENDING(numbers)
[2, 3, 4, 5, 6, 7, 8, 9]
>>> Sort.DESCENDING(numbers)
[9, 8, 7, 6, 5, 4, 3, 2]
当我们定义枚举类时,让其同时继承int和Enum,它就具备了与数字比较的能力,即>
,<
和==
操作符。
>>> from enum import Enum
>>> class Size(int, Enum):
... S = 1
... M = 2
... L = 3
... XL = 4
...
>>> Size.S > Size.M
False
>>> Size.S < Size.M
True
>>> Size.L >= Size.M
True
>>> Size.L <= Size.M
False
>>> Size.L > 2
True
>>> Size.M < 1
False
但enum模块其实已经帮我们定义了一个IntEnum类供我们使用。
>>> from enum import IntEnum
>>> class Size(IntEnum):
... S = 1
... M = 2
... L = 3
... XL = 4
...
>>> Size.S > Size.M
False
>>> Size.S < Size.M
True
>>> Size.L >= Size.M
True
>>> Size.L <= Size.M
False
>>> Size.L > 2
True
>>> Size.M < 1
False
除此之外,还有Flag和IntFlag类型。
>>> from enum import IntFlag
>>> class Role(IntFlag):
... OWNER = 8
... POWER_USER = 4
... USER = 2
... SUPERVISOR = 1
... ADMIN = OWNER | POWER_USER | USER | SUPERVISOR
...
>>> john_roles = Role.USER | Role.SUPERVISOR
>>> john_roles
<Role.USER|SUPERVISOR: 3>
>>> type(john_roles)
<enum 'Role'>
>>> if Role.USER in john_roles:
... print("John, you're a user")
...
John, you're a user
>>> if Role.SUPERVISOR in john_roles:
... print("John, you're a supervisor")
...
John, you're a supervisor
>>> Role.OWNER in Role.ADMIN
True
>>> Role.SUPERVISOR in Role.ADMIN
True
IntFlag比Flag多了一个直接与数字进行比较和加减运算的能力。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?