08 固定枚举类与闭包之一


目录

  • 固定枚举类

    • 枚举正确写法
    • 相比普通类,枚举的优势
    • 获取枚举值、枚举标签名、枚举类型
    • 枚举类型与遍历
    • 枚举类型与运算
    • 枚举值相同时的特殊情况
    • 枚举类型转换
    • 枚举类的父类IntEnum
    • @unique装饰器
    • 拓展
  • 闭包

一、固定枚举类

之前,学习的面向对象中的类,其实例化的对象是无限制的,例如,将学生类实例化,可以得到小明、小红、小刚...。
但是,现实世界有种特殊的类,其对象却是有限且固定的。例如,月份类的对象数目只能是1-12,星期类的对象数目只能是1-7。所以叫固定枚举类。

所以python3.4就新增Enum父类,专门描述固定枚举类。
其中,Enumerate:one by one

所以,枚举是之前面向对象编程中的普通类的一种补充。
1.枚举的正确写法
(1)一般写法:
用不同的数字表示不同的种类
例如:表示腾讯QQ的不同种类的会员?

1  绿钻
2  黄钻
3  红钻
4  黑钻

缺点:当别人看到数字时,是不知道其是什么类型的,不直接。也就是说,会破坏可读性。

(2)正确写法:
用标签名字,定义一组常量,以文字形式来表示不同的种类

from enum import Enum       # Enum是个内置的类,且是父类(第六章)
class VIP(Enum):         #所有的枚举类,都是Enum的子类
    YELLOW = 1
    GREEN = 2     #左边:有意义的标签名字         右边:没意义的取值
    BLACK = 3     #左边:最好用大写,因为是常量    右边:只要不同就行,随便改
    RED = 4
print(VIP.YELLOW)        

-->
VIP.YELLOW     #不是数字1?这正是枚举类的意义所在:重在标签名字

2.相比普通类,枚举的优势
(1)关于表示枚举类,常见的三种写法:

写法1:
通过模块中的全局变量来实现

yellow = 1
green = 2

写法2:
用数据结构字典dict来组织数据

{'yellow':1,'green':2}    #如果不用枚举类,算是很好的一个表示种类的方式(最贴近枚举类)

写法3:
用类来封装数据

class TypeDiamond():
    yellow = 1       #定义一个普通的类,将其作为类变量封装起来
    green = 2

小结:
写法1的缺点,之前已分析
写法2与写法3的缺点:

  • 可变
    代码中可轻易更改它的值
  • 没有防止相同标签的功能
    可轻易重复

(2)枚举类的优势

优势1:不能更改枚举类的数值

from enum import Enum      
class VIP(Enum):         #枚举类       
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        #普通的类
    YELLOW = 1         #普通类下的类变量,随便改

print(VIP.YELLOW)        
VIP.YELLOW = 6         #通过赋值,试试改下枚举类的数值

-->
error ❌     #优势1:不能更改枚举类,这是一种保护功能

优势2:不能重复使用相同的标签

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    YEELOW = 2       #试试让两个标签名字相同
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

print(VIP.YELLOW)        
VIP.YELLOW = 6         

-->
error ❌     #不能重复使用标签名字

3.获取枚举值、枚举标签名、枚举类型

(1)如何获取枚举类型下面,某一个标签所对应的具体的数值?
print(VIP.GREEN.value)获取

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

print(VIP.GREEN.value)       #枚举的值

-->
2

(2)如何获取枚举类型下面,某一个标签的名字?

print(VIP.GREEN.name)获取

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

print(VIP.GREEN.name)       #枚举的名字

-->
GREEN

(3)如何获取枚举类型本身?

直接print(VIP.GREEN)获取

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

print(VIP.GREEN)           #枚举类型本身  

-->
VIP.GREEN

(4)同样是GREEN,(2)与(3)有什么区别?
数据类型不一样。

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

print(type(VIP.GREEN.name))     
print(type(VIP.GREEN))           

-->
<class 'str'>       #name只是标签的名字,所以是字符串
<enum 'VIP'>        #整体表示枚举类型

(5)如何通过枚举标签的名称,获取枚举类型?
通过print(VIP['GREEN'])获取,有点像字典的访问

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

print(VIP['GREEN'])           

-->
VIP.GREEN

4.枚举类型与遍历
用for循环,遍历所有的枚举类型

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

for v in VIP:
    print(v)

-->
VIP.YELLOW        #遍历出了所有的枚举类型
VIP.GREEN
VIP.BLACK
VIP.RED

5.枚举类型与运算

(1)枚举类型之间支持等值比较运算

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

result = VIP.GREEN == VIP.BLACK     #值是否相等
print(result)

-->
False                       #不相等
from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

result = VIP.GREEN == VIP.GREEN    #值是否相等
print(result)

-->
True                        #相等

当然,也可以是这样的等值比较:

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class VIP1(Enum):        #成了枚举类型
    YELLOW = 1        

result = VIP.YELLOW == VIP1.YELLOW    #值是否相等
print(result)

--> 
False                       #不相等

注意:

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

result = VIP.GREEN == 2     #必须是两个枚举类之间进行比较,不能是数字int
print(result)

-->
False    #虽不报错,但却是错误的❌

(2)枚举类型之间不支持大小比较的运算

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

result = VIP.GREEN >= VIP.BLACK   #大小比较?   
print(result)

-->
error ❌   

(3)枚举类型之间支持身份运算

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 2     
    BLACK = 3     
    RED = 4

class Commen():        
    YELLOW = 1        

result = VIP.GREEN is VIP.GREEN    #身份是否相同
print(result)

-->
True

6.枚举类型的值相同时的特殊情况
由上可知,枚举类型的标签名称一定不能相同。那么,枚举类型的值能否相同呢?
值能相同,但会被python视为第一个标签的别名。
本质上,两者仍是同一种枚举类型。

(1)例题

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 1       #GREEN标签的值,跟第一个YELLOW标签的值,一样了    
                  #本质上:YELLOW_ALIAS = 1
    BLACK = 3     
    RED = 4

print(VIP.GREEN)     #看下GREEN的枚举类型的变化

-->
VIP.YELLOW          #既然两者一样,那就只保留大名YELLOW,剔除GREEN
from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 1              
    BLACK = 3     
    RED = 4

for v in VIP:        #用for循环遍历时
    print(v)

-->
VIP.YELLOW
VIP.BLACK
VIP.RED       #第二个值重复的枚举类型GREEN,不会被打印出来 

(2)如果非要遍历打印出别名呢?

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 1              
    BLACK = 3     
    RED = 4

for v in VIP.__members__.items():    #使用内置变量__members__,并调用items方法        
                             #⭐遍历字典时,也是用的该法(第几章)
    print(v)

-->
('YELLOW', <VIP.YELLOW: 1>)     #以元组的形式
('GREEN', <VIP.YELLOW: 1>)     #别名的枚举类型,也打印出来了
('BLACK', <VIP.BLACK: 3>)
('RED', <VIP.RED: 4>)          #看着有点复杂

from enum import Enum      
class VIP(Enum):              
    YELLOW =         
    GREEN = 1              
    BLACK = 3     
    RED = 4

for v in VIP.__members__:    #只使用内置变量__members__
    print(v)

-->
YELLOW          #精简
GREEN
BLACK
RED        #不管值有没有重复,左边的“标签名称”都打印出来了

7.枚举转换

编写服务器代码时,要与数据库打交道。在数据库中,存取枚举类型两种方式:

  • 一般存取具体的数值(数字)
  • 也可以存取标签名字,将其以字符串的形式,存到数据库中某一字段中,以代表枚举类型。

建议第一种方式。

if a==1:        #这里的1和2代表什么?虽然逻辑上没问题,但可读性差
    print()
if a==2:        #即使加上注释,也不建议该写法
    print()
if a==VIP.YELLOW:
    print()
if a==VIP.BLACK:        #可读性好
    print()

数据库已存数值(数字),代码也已定义枚举类。那么如何将数字转换成枚举类型?

from enum import Enum      
class VIP(Enum):              
    YELLOW = 1
    GREEN = 1              
    BLACK = 3     
    RED = 4

class common():
    YELLOW = 1

a = 1
print(VIP(a))     #将数字传入到VIP()中即可     #既可以传左边标签名,又可以传右边数字

-->
VIP.YELLOW    #打印出的是枚举类型

上述的将数字转换成枚举类型,并非真正数据类型的转换(str-->int),仅仅只是使用数值来访问对应的枚举类型。因此,只是视作转换。

8.枚举类的父类IntEnum

以前,枚举类的数值既可以是整型int,也可以是字符串str,更可以是其他。
但如果想强制要求只能是整型int呢?

枚举类的父类,除了Enum,还有IntEnum。
用途:限制枚举类型的数值只能是整型int

例题

from enmu import IntEnum

class VIP(IntEnum):       #这里导入的父类是IntEnum
    YELLOW = 1
    GREEN = 'str'        #只能是整型int

-->
error ❌

9.@unique装饰器
用途:限制每个不同的枚举类型,不能取相同的值

例题

from enum import Enum,unique

@unique
class VIP(Enum):       
    YELLOW = 1
    GREEN = 1        #枚举类型的数值,跟上面一样
    black = 3   

-->
error ❌            #装饰器@unique规定,数值不能相同

10.拓展
枚举类型与单例模式

枚举类型的实现是一种单例模式,单例模式是23种设计模式之一。即它无法像普通的类一样实例化。

(1)设计模式的初衷:

  • 防止代码频繁变化。

(2)设计模式的特点:

  • 对团队成员要求高
  • 初始成本高
  • 其优势,日后很长时间才能凸显

(3)什么时候建议用设计模式,什么时候不用?

使用设计模式不使用设计模式
产品,平台,服务小项目
生命周期很长生命周期短
纯粹靠业务逻辑支持,没太多技术含量的项目

业务变更,只需要改变一个变量即可。若套用设计模式,还要补充一个类。
所以,编程初期,不建议过分关注。

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 08 固定枚举类与闭包之一


Follow excellence, and success will chase you.