10、枚举与闭包

枚举

枚举类:
from enum import Enum  #导入枚举类

class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4
 
print(VIP.YELLOW)

结果:VIP.YELLOW    #关注的是它的标签而不是数字
枚举和普通类相比有什么优势
三种其他的普通方法表示枚举:
1)
yellow = 1
green = 2
2)
{'yellow':1,'green':2}    
3)
class TypeDiamond():
    yellow = 1
    green = 2

特点:他们都是可变的,可以在代码中轻易的更改值,且没有防止相同标签的功能。
枚举的特点:
from enum import Enum

class VIP(Enum):
    YELLOW = 1
    YELLOW= 2   #不可重复,否则报错
    BLACK = 3
    RED = 4

VIP.YELLOW = 6    #枚举类内的数据不可更改,否则会报错
枚举类型、枚举名称与枚举值
获取枚举变量的数值:
from enum import Enum

class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4
   
print(VIP.YELLOW.value)  #获取变量的数值
结果:1

print(VIP.YELLOW.name)  #获取变量的名称
结果:YELLOW   #str类型

print(VIP.YELLOW)   #获取枚举变量 
print(VIP['YELLOW'])
结果:VIP.YELLOW  #<enum 'VIP'>

print(type(VIP.YELLOW))  #获取获取变量类型
结果:<enum 'VIP'>


枚举是可以遍历的:
for v in VIP :
    print(v)
结果:
VIP.YELLOW
VIP.GREEN
VIP.BLACK
VIP.RED

枚举的比较运算

  • 两个枚举之间可以使用等值比较(==),但不能进行大小比较。
  • 支持身份验证(is操作):result = VIP.BLACK is VIP.GREEN
两个枚举类的变量之间也可以进行等值比较,不过结果是False:
from enum import Enum

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

class VIP1(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

print(VIP.GREEN ==VIP1.GREEN)
结果:False

枚举注意事项

枚举的数值可以相同,在这种情况下,将第二种枚举类型看成别名。遍历时不会打印别名:
class VIP(Enum):
    YELLOW = 1
    GREEN = 1    #别名,不会报错
    BLACK = 3
    RED = 4

#把别名加入遍历循环:
for v in VIP.__members__.items() :  #获取枚举类成员的具体信息
    print(v)
结果:
('YELLOW', <VIP.YELLOW: 1>)
('GREEN', <VIP.GREEN: 1>)
('BLACK', <VIP.BLACK: 3>)
('RED', <VIP.RED: 4>)


遍历__members__:
for v in VIP.__members__:   #获取枚举类的成员
    print(v)
结果:
YELLOW
GREEN
BLACK
RED

枚举转换

在数据库里一般存储数值或者标签名字来代表枚举类型,推荐存储数值.数字占用的空间更小。但是不建议在代码中用数值代表枚举,可读性不强。

如何将数字转换成枚举类型:
from enum import Enum

a = 1
class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

print(VIP(a))    #转换枚举类型

结果:VIP.YELLOW

数字枚举

要求每个枚举类型都是数字的时候继承IntEnum:
from enum import IntEnum

class VIP(IntEnum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

限制不同的枚举类型不能取相同的值:
from enum import IntEnum,unique

@unique    #装饰器
class VIP(IntEnum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

枚举类型不能实例化,属于单例模式

进阶内容

业务逻辑的开发者,不要考虑太多的封装性
包和类库的开发者,要考虑封装性

一切皆对象

python中的函数是对象,一切皆对象。可以把函数赋值给变量:

a = 1
a = '2'
a = def

甚至可以把函数当作另外一个函数的参数传递或者当成返回值返回。

闭包

内部函数
def curve_pre():
    def curve():
        pass

curve()    #报错,因为curve()的作用域仅限于curve_pre()的内部


闭包
def curve_pre():
    a = 25 
    def curve(x): 
        return a * x * x  #局部变量找不到会到上一级找
    return curve #返回一个函数

f = curve_pre()
print(f(2)) #调用curve()函数
结果:100
外部变量不会影响到闭包
def curve_pre():
    a = 25   #局部变量在curve的外部
    def curve(x):   #接受抛物线的x值
        return a * x * x
    return curve   #返回一个函数

a = 10     #定义a = 10
f = curve_pre()
print(f(2)) #调用curve()函数

结果:100        #仍然是a = 25的取值,取得是定义时的环境变量,这就是闭包
  • 函数及其外部环境变量所构成的整体叫做闭包
  • 环境变量要在函数外部,但不能是全局变量。
错误的闭包示范(使用全局变量)
a = 25    #a定义为了全局变量
def curve_pre():
    def curve(x): #接受抛物线的x值
        return a * x * x
    return curve #返回一个函数

a = 10
f = curve_pre()
print(f(2)) #调用curve()函数

结果:40    #a的值被改变了
查看闭包:
f = curve_pre()
print(f.__closure__)  #闭包函数,如果不是闭包会报错
print(f.__closure__[0].cell_contents)

结果:
(<cell at 0x0031AAF0: int object at 0x0FF93A80>,)  #闭包的存储地址
25    #获取环境变量 a

闭包理解

个人理解:闭包就是一个封闭的环境,这里我们使用函数实现,当外部调用函数。外侧函数(变量+内函数),需要返回内函数。此时才可以调用这个闭包。
闭包函数和普通函数最大的区别就是返回值不是数值。

内函数,没有使用环境变量,会出错。
def f1():
    a = 10
    def f2():
        a = 20    #a被认为是一个局部变量了,就不认为是个环境变量了
        return a
    return f2
 
f = f1()
print(f.__closure__)    #没有__closure__属性
结果:None

正确的闭包示范:
def f1():
    a = 10
    def f2():
        return a
    return f2

f = f1()
print(f.__closure__)
结果:(<cell at 0x02F5AAF0: int object at 0x0FF93990>,)

环境变量不能当作一个变量去赋值,而是一定要去引用外函数的变量(这里与我们定义类时,十分相似)。

旅行者问题

x = 0 为起点,每次计算出旅行者当前所处的位置。

使用全局变量实现:
origin = 0

def go(step):
    global origin    #将origin变成全局变量
    new_pos = origin + step
    origin = new_pos  #如果不提前声明,这一步就会被当作命名新的局部变量,而上一步提前调用了局部变量,从而报错。
    return origin

print(go(2))
print(go(3))
print(go(6))
结果:
2
5
11

使用闭包实现:
origin = 0
def factory(pos):    #工厂模式
    def go(step):
        nonlocal pos #强制声明不是局部变量
        new_pos = pos + step
        pos = new_pos
        return new_pos
    return go

tourist = factory(origin)
print(tourist(2))
print(tourist(3))
print(tourist(6))
结果:
2
5
11
此时并没有改变全局变量origin的值
    原文作者:IT_Freak
    原文地址: https://www.jianshu.com/p/f3816c0fa77b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞