10、枚举与闭包
2018-12-24 本文已影响0人
IT_Freak
枚举
枚举类:
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的值