enum -- 枚举(三)

前情提示: 测试代码中,右尖括号(>)表示命令行中输入的命令; 单独一行并以井字符(#)开头的为输出内容; 库的导入仅在本文的第一个测试代码中展现,其他代码块均省略库的导入代码。

  • 系统类型: Windows 10
  • python 版本: Python 3.9.0

前两篇文章详解了枚举模块中的四个枚举类和自定义枚举类及其相关内容,没看过的小伙伴可以通过传送门点击进入前两篇文章阅读。枚举模块的最后一篇来更加深入的了解一下比较深入并且不常用的一些知识点。

传送门

enum -- 枚举(一)

enum -- 枚举(二)

enum -- 枚举(三)

不重要的枚举成员的值

在某些场景中,人们并不关心枚举成员的值是什么,可以使用下列几种方法定义枚举:

  • 使用 enum.auto() 函数作为枚举成员的值
  • 使用 object() 作为枚举成员的值
  • 使用描述性的字符作为枚举成员的值,同时做到了可进一步解释此枚举成员
  • 使用空元组作为值,并在自定义的 __new__() 中重新定义枚举成员的值(只要不重复即可)

看完以上的列举,这种实现这种需求并不局限于上面几种方法,只要最终枚举成员的值不重复即可,比如自定义一个随机生成字符串的函数。

import enum
import pickle  # 封存


'''enum.auto()'''
class TestA(enum.Enum):
    A = enum.auto()
    B = enum.auto()

'''object()'''
class TestB(enum.Enum):
    A = object()
    B = object()

'''描述字符串'''
class TestC(enum.Enum):
    A = '字母A'
    B = '字母B'

'''元组, 并自定义 __new__()'''
class TestD(enum.Enum):
    A = ()
    B = ()

    def __new__(cls, *args):
        '''获取当前枚举成员数并加一, 将结果赋值给新定义的枚举成员'''
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)  # 必须创建一个枚举成员对象
        obj._value_ = value
        return obj  # 必须返回一个枚举成员对象

print(list(TestA))
# [<TestA.A: 1>, <TestA.B: 2>]
print(list(TestB))
# [<TestB.A: <object object at 0x0000023986178210>>, <TestB.B: <object object at 0x0000023986178220>>]
print(list(TestC))
# [<TestC.A: '字母A'>, <TestC.B: '字母B'>]
print(list(TestD))
# [<TestD.A: 1>, <TestD.B: 2>]
封存

枚举可以被封存和解封:

class Test(enum.Enum):
    A = enum.auto()
    B = enum.auto()

封存的枚举成员A = pickle.dumps(Test.A)
解封的枚举成员A = pickle.loads(封存的枚举成员A)

print(封存的枚举成员A)
# b'\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04Test\x94\x93\x94K\x01\x85\x94R\x94.'
print(解封的枚举成员A)
# Test.A
print(Test.A is 解封的枚举成员A)  # 解封后的数据与原数据是否一致
# True
功能性API

Enum 类属于可调用对象,也就是说 Enum 类可以像函数一样被调用:

Enum(value='NewEnumName', names=<...>, *, module='...', qualname='...', type=<mixed-in class>, start=1)

参数:
    value: str, 枚举的名称
    names: 枚举成员们的名称, 可使用多种方式设置枚举成员
    module: 关键字参数, str 或 None, 指明所在的模块
    qualname: 关键字参数, str, 指明所在的模块中的具体位置
    type: 关键字参数, 指明枚举类的类型
    start: 关键字参数, int, 枚举成员的起始值, 默认为 1
返回值:
    枚举

names 参数可以传入一个以空格或逗号隔开的字符串,分隔的子字符串就是枚举成员的名称,各枚举成员的值按顺序自增,起始值由 start 参数设置,起始值默认为 1names 参数也可以传入迭代器或映射。

'''以空格分隔'''
TestA = enum.Enum('TestEnum', 'A B C D')
print(list(TestA))
# [<TestEnum.A: 1>, <TestEnum.B: 2>, <TestEnum.C: 3>, <TestEnum.D: 4>]

'''以空格分隔, 设置起始值为10'''
TestB = enum.Enum('TestEnum', 'A B C D', start=10)
print(list(TestB))
# [<TestEnum.A: 10>, <TestEnum.B: 11>, <TestEnum.C: 12>, <TestEnum.D: 13>]

'''传入列表设置枚举成员, 枚举成员的值按顺序自增, 起始值由 start 参数设置'''
TestC = enum.Enum('TestEnum', ['A', 'B', 'C', 'D'])
print(list(TestC))
# [<TestEnum.A: 1>, <TestEnum.B: 2>, <TestEnum.C: 3>, <TestEnum.D: 4>]

'''传入列表, 并且元素格式为 ('名称', '值')'''
TestD = enum.Enum('TestEnum', [('A', 11), ('B', 22), ('C', 33), ('D', 44)])
print(list(TestD))
# [<TestEnum.A: 11>, <TestEnum.B: 22>, <TestEnum.C: 33>, <TestEnum.D: 44>]

'''传入映射, 格式为 {名称: 值}'''
TestE = enum.Enum('TestEnum', {'A': 11, 'B': 22, 'C': 33, 'D': 44})
print(list(TestE))
# [<TestEnum.A: 11>, <TestEnum.B: 22>, <TestEnum.C: 33>, <TestEnum.D: 44>]

module 参数需要传入一个模块,为了在进行封存操作时成功执行。若指定一个错误的模块,那么封存操作将报错。若参数值若设置为 None__name__ 最终指向的模块均为当前文件 __main__

'''module 参数默认为 None'''
TestA = enum.Enum('TestA', 'AAA BBB CCC')
print(pickle.loads(pickle.dumps(TestA)))  # 封存+解封
# <enum 'TestA'>

'''module 参数设置为 __name__'''
TestB = enum.Enum('TestB', 'AAA BBB CCC', module=__name__)
print(pickle.loads(pickle.dumps(TestB)))
# <enum 'TestB'>

'''module 参数设置为一个不存在的模块'''
TestD = enum.Enum('TestD', 'AAA BBB CCC', module='aaa')
print(pickle.loads(pickle.dumps(TestD)))
# _pickle.PicklingError: Can't pickle <enum 'TestD'>: import of module 'aaa' failed

qualname 参数是指明在模块中的具体位置,同 module 参数类似,设置这个参数的目的也是为了封存操作,pickle 协议版本 4 在某些情况下需要依赖设置的具体位置。

布尔值

枚举类的布尔值总是 True,枚举中的枚举成员的布尔值也总是 True。 枚举成员的布尔值与枚举成员的值无关,如果想要以枚举成员的值来计算布尔值,可以修改 __bool__() 魔法方法。

'''不修改 __bool__() 魔法方法的枚举类'''
class Test(enum.Enum):
    A = 0
    B = None

print(bool(Test))
# True
print(bool(Test.A))
# True
print(bool(Test.B))
# True

'''修改 __bool__() 魔法方法的枚举类'''
class Test(enum.Enum):
    A = 0

print(bool(Test.A))
# False
支持枚举类使用的属性

__members__ 是一个 member_name:member 条目的只读有序映射。得到的结果是以 {名称: 枚举成员对象} 为结构的字典。

class Test(enum.Enum):
    A = 1
    B = 2
    C = 1

print(Test.__members__)
# {'A': <Test.A: 1>, 'B': <Test.B: 2>, 'C': <Test.A: 1>}
支持枚举成员使用的属性
  • _name__value_ 分别返回枚举成员的名称和值;
  • _generate_next_value_ 在功能性API中被使用,为枚举成员获取适当的值,也可被打印;
  • _missing_作用是当未发现某个值时所使用的查找函数;
  • _ignore_ 是一个名称列表,结果可以是 list,也可以是 str,不会转化为枚举成员,并将从最终类中被移除;
  • _order_Python2 的遗留属性,在 Python2 中负责记录枚举成员的定义顺序;
'''可直接打印的属性'''
class Test(enum.Enum):
    A = 1
    B = 2
    C = 1

print(Test.A._name_)
# 'A'
print(Test.A._value_)
# 1
print(Test.A._generate_next_value_)
# <bound method Enum._generate_next_value_ of <Test.A: 1>>

'''_ignore_的用法'''
class Period(enum.Enum):
    _ignore_ = 'Period i'
    Period = vars()
    for i in range(3):
        Period['obj_%d' % i] = i

print(list(Period))
# [<Period.obj_0: 0>, <Period.obj_1: 1>, <Period.obj_2: 2>]

公众号 : 「python杂货铺」,专注于 python 语言及其相关知识。发掘更多原创文章,期待您的关注。

参考资料

官方文档: https://docs.python.org/zh-cn/3/library/enum.html

源代码: Lib/enum.py

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 前情提示: 测试代码中,右尖括号(>)表示命令行中输入的命令; 单独一行并以井字符(#)开头的为输出内容; 库的导...
    暗语寒飞阅读 378评论 0 0
  • Python 廖雪峰: https://www.liaoxuefeng.com/wiki/001431608955...
    我的钱包瘪瘪的阅读 546评论 0 0
  • 前情提示: 测试代码中,右尖括号(>)表示命令行中输入的命令; 单独一行并以井字符(#)开头的为输出内容; 库的导...
    暗语寒飞阅读 302评论 0 0
  • 一直没有时间好好看一下swift,最近复习了一遍语法,这里记录swift学习过程中遇到的一些问题和要点,和Obje...
    bomo阅读 2,332评论 0 25
  • 转自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志阅读 24,762评论 2 38