我是靠谱客的博主 坦率棉花糖,这篇文章主要介绍我们为什么要用枚举类?从产品经理的角度,手把手带你走进enum的神奇世界1. 不使用枚举类的阶段2. 有枚举思想的阶段3. 使用枚举的阶段深入了解枚举阶段,现在分享给大家,希望可以做个参考。

枚举类作为在项目规模扩大时必须使用的类,其重要性不言而喻。但是有很多时候,由于它的麻烦写法,让很多人容易顺手写个常量或者字符串来代替其语义。我们说编程也是需要语义化编程,也就是当我们编程完毕后,代码本身就像注释一般,可以也很容易的让我们理解代码的含义。

因此,这里,我们从产品经理的角度,分析为什么要使用枚举类的3大需求,使用方法以及2个扩展特性,并在最后进行一个深入的源码拓展。

学习任何一个东西,尤其是语言,不仅仅弄清是什么,更要弄清为什么,尽管我们可能花费更多的时间思考,但是这样记的更牢,理解的更透彻。学习英语是如此,学习编程,也是如此。

1. 不使用枚举类的阶段

枚举类在初学者看来是一个相当鸡肋的类。首先,在我们进行选择、比较的时候,我们更喜欢直接赋值变量,以int或者string类型的最多,比如当我们定义一个学生类的性别的时候,我们更喜欢使用male 或者female来进行赋值。

复制代码
1
2
3
4
5
class student: def __init__(self): self.gender="male" # or female ...

或者当我们进行判断时,我们又长又乱的if-else语句其判断条件都是手写的赋值,这样一方面难以理解其含义,另一方面,你很难控制你的data_num赋值时能够赋予他合法的值,而且执行无误,因为我们总会有一个else兜底。

复制代码
1
2
3
4
5
6
7
8
9
10
if data_num=1: print() elif data_num=2: write() elif data_num=3: exec() ... else: unexception()

因此,我们可能有以下3个需求需要用到枚举:

  1. 当我们需要一个固定的变量时,我们能够很好的记住所有的选项。
  2. 当我们赋予一个可能具有多个离散值的变量时,我们希望它是合法并且符合预期的。
  3. 当我们阅读到一个具有多个离散值的变量时,我们需要能够非常容易的理解其含义。

2. 有枚举思想的阶段

有了这些需求,程序员就开始动脑筋了,我们该如何能够实现刚才的需求呢?作为天才的你一定发现了,我们可以使用python中的字典或者类来实现刚才的功能。

2.1 使用字典实现

比如,当我们需要一个颜色枚举的时候,我们就使用字典实现,如下代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 使用字典 Color = { 'RED': 1, 'GREEN': 2, 'BLUE': 3, } # 赋值一个图片的元素的例子 class Picture: def __init__(self): self.sun_color = Color["RED"] self.sky_color = Color["BLUE"] self.grass_color = Color["GREEN"] def update_sun_color(self, color): self.sun_color = color if __name__ == '__main__': custom_picture = Picture() new_color = Color["GREEN"] custom_picture.update_sun_color(new_color) print(custom_picture.sun_color) # 2

那么这样的改变,满足了刚才的几个需求呢?回过头看,我们看到主要满足了第2和第3条需求,第一条需求仍然没有被满足。因为我们需要一个能够有可选项的编码,而不是自己填。因此使用类也可以解决这个问题。

2.2 使用类实现

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Color: RED = 1 GREEN = 2 BLUE = 3 # 赋值一个图片的元素的例子 class Picture: def __init__(self): self.sun_color = Color.RED self.sky_color = Color.BLUE self.grass_color = Color.GREEN def update_sun_color(self, color): self.sun_color = color if __name__ == '__main__': custom_picture = Picture() new_color = Color.GREEN custom_picture.update_sun_color(new_color) print(custom_picture.sun_color) # 2

这样看似问题都解决了,也能够可读,也能够候选。

但是其实它还有两个缺陷,第一个是,它的第二条会在比较时发生问题,即如果出现了预期之外的值的时候,没有办法处理,比如我们这里就只有3个颜色,如果我们新的值是5,我们就很难处理。常见的处理方法比如加一个default用来表示其他值,但是有可能是无穷尽个不符合预期的值呢?第二个,这个属性的值是会被改变的,下面一个例子就揭示了一个非常吓人的场景。

复制代码
1
2
3
4
5
6
7
8
if __name__ == '__main__': custom_picture = Picture() Color.GREEN = 3 # 新加的一句话 new_color = Color.GREEN custom_picture.update_sun_color(new_color) print(custom_picture.sun_color) # 3 print(custom_picture.sun_color == custom_picture.sky_color) # 非常诡异的True

上面的例子可以看出,我们只需要增加一句话,太阳和天空的颜色居然是一样的,尽管我们赋值时,以为自己赋予的是绿色!因此,枚举类应运而生了。它将满足上面的三个需求,我们会一一讲解。

3. 使用枚举的阶段

综合上面的例子,我们可以看到,枚举真的非常有必要使用,曾经不使用枚举的我,陷入了很多代码的巨坑!回归正题,我们首先看一个枚举类的例子。

复制代码
1
2
3
4
5
6
7
8
from enum import Enum class Color(Enum): RED = 1 GREEN = 2 BLUE = 3

我们只需要继承一个枚举类,就可以完全的避免刚才普通类出现的问题,当你再次非法赋值的时候,你会收到如下的警告:

复制代码
1
2
3
raise AttributeError('Cannot reassign members.') AttributeError: Cannot reassign members.

因此就可以避免非法的重新赋值的场景,能够满足1,2,3条需求。当然你也可以用以下语句更加便捷的创建一个枚举类:

复制代码
1
2
Color = Enum("Color",('RED','GREEN ','BLUE '))

3.1 枚举类的使用与特性

刚才我们主要见识了枚举类和一般类的区别和样子,我们现在看看如何正确使用枚举类,还是以刚才的例子:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from enum import Enum # 创建一个枚举类 class Color(Enum): RED = 0 GREEN = 1 BLUE = 2 blue = 2 # 别名 #调用枚举成员的 3 种方式 print(Color.RED) # Color.RED print(Color['RED']) # Color.RED print(Color(1)) # Color.GREEN #调取枚举成员中的 value 和 name print(Color.RED.value) # 0 print(Color.RED.name) # RED #遍历枚举类中所有成员的 2 种方式 for color in Color: print(color) # Color.RED Color.GREEN Color.BLUE for color in Color.__members__: print(color) # RED GREEN BLUE blue

从上面我们可以看到,枚举类甚至可以做出不同名,同值的情况,第二个名称会被认定为别名,在普通遍历时,是不会被看到的。

当我们幻想着有其他非法值时print(Color(3)),它会和字典一样自动弹出异常:

复制代码
1
2
3
raise ValueError("%r is not a valid %s" % (value, cls.__name__)) ValueError: 3 is not a valid Color

3.2 枚举类的扩展特性

通过刚才的讲解,我们知道枚举类除了满足我们的3大需求外,展现出两个特性:不唯一性不可比较性。如果想要打破这两个特性,我们就不得不使用一些手段了。

1. 唯一性

如果我们想要让枚举变得唯一,只需要增加一个独一性的装饰器即可:

复制代码
1
2
3
4
5
6
7
8
9
10
11
# 创建一个唯一性的枚举类 from enum import Enum, unique @unique class Color(Enum): RED = 0 GREEN = 1 BLUE = 2 blue = 2

此时就会报错:

复制代码
1
2
3
(enumeration, alias_details)) ValueError: duplicate values found in <enum 'Color'>: blue -> BLUE

2. 可比较性

枚举的成员可以通过 is 同一性比较或通过 == 等值比较,但是不能够比较大小,其原因就是我们这个枚举类非常的基础,只是从Object类继承,而Object类确实就只能进行等于比较,不能进行大小比较,不过好在我们又更加细致的类IntEnum,当你需要进行数值比较的时候,可以实现这个类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from enum import IntEnum class Shape(IntEnum): circle = 1 square = 2 class Request(IntEnum): post = 1 get = 2 print(Shape.circle == 1) # True print(Shape.circle < 3) # True print(Shape.circle == Request.post) # True print(Shape.circle >= Request.post) # True

深入了解枚举阶段

看到枚举类这么神奇,我们就不想看一看枚举到底是如何实现的么?下面给出一个自我实现的枚举和官方的部分源码,由于这里是更加深层次的理解,详情请看《python中enum模块源码的详细分析》。

自我实现一个枚举

就像我们改进字典的缺陷一样,我们可以基于字典构建一个枚举,这里还可以使用__prepare__魔术方法返回一个类字典实例,但是这个枚举仍然没有解决可以重新赋值的问题。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class _Dict(dict): def __setitem__(self, key, value): if key in self: raise TypeError('Attempted to reuse key: %r' % key) super().__setitem__(key, value) class MyMeta(type): @classmethod def __prepare__(metacls, name, bases): d = _Dict() return d class Enum(metaclass=MyMeta): pass class Color(Enum): red = 1 red = 1 # TypeError: Attempted to reuse key: 'red'

官方源码解析

官方的代码就强大和复杂的多,下面是部分类及其实现,我们仍然能够看到刚才的想法的影子。另一个我们可学习的是,当我们一个类特别的复杂时,我们需要将其分解为一个有一个小类,注意的是,类的方法只能操纵类原有的属性,如果要在类的更上一层进行操作和管理时,可以再创建一个更高级的管理者来管理我们所需要的类;而当你需要将类进入到不同的场景以细化时,可以使用继承出子类进行操作,省去很多重复代码。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class _EnumDict(dict): def __init__(self): super().__init__() self._member_names = [] ... def __setitem__(self, key, value): ... elif key in self._member_names: # descriptor overwriting an enum? raise TypeError('Attempted to reuse key: %r' % key) ... self._member_names.append(key) super().__setitem__(key, value) class EnumMeta(type): @classmethod def __prepare__(metacls, cls, bases): enum_dict = _EnumDict() ... return enum_dict class Enum(metaclass=EnumMeta): ...

最后

以上就是坦率棉花糖最近收集整理的关于我们为什么要用枚举类?从产品经理的角度,手把手带你走进enum的神奇世界1. 不使用枚举类的阶段2. 有枚举思想的阶段3. 使用枚举的阶段深入了解枚举阶段的全部内容,更多相关我们为什么要用枚举类?从产品经理的角度,手把手带你走进enum的神奇世界1.内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部