from enum import Enum
class VIP(Enum):
'''QQ钻石会员'''
RED = 1 # 变量名要大写
YELLOW = 2
GREEN = 3
BLACK = 4
PINK = 5
class City(Enum):
BEIJING = 'Beijing' # 值类型不限
SHANGHA = 'Shangha'
SHENZHEN = 'Shenzhen'
GUANGZHOU = 'Guangzhou'
使用枚举和访问类变量方式相同,class_name.type_name
。注意,访问枚举类型的得到的就是类型名本身,而不是类变量的值,这也正是枚举类的意义所在。
print(VIP.RED)
class Fake_VIP(object):
RED = 1
print(Fake_VIP.RED) # 得到的是类变量的值
有人或许会说可以使用字典 key:value
表示类型,但字典和定义类变量最大的问题是不安全,它们都可以随意修改“类型值”
Fake_VIP.RED = 1 # 普通的赋值操作
VIP.RED = 9 # 枚举类型不允许修改类型值
通过 enum.type_name.name
获取枚举类型的名称,得到的是名称字符串。通过 enum.type_name.value
获取类型值
class Call(Enum):
COP = 110
FIRE = 119
ICU = 120
Call.COP.name
Call.COP.value
print(type(Call.COP.name)) # 类型名称字符串
print(type(Call.COP)) # 得到的是枚举类型
如果知道类型名称可以通过类型名称获取对应的枚举类型
Call['COP']
枚举类型可以遍历
for type_name in Call:
print(type_name)
枚举类型支持身份比较(is)和等值比较(==),不支持大小比较
class Num(Enum):
ONE = 1
TWO = 2
THREE = 3
FOUR = 1
Num.ONE == Num.TWO
Num.ONE == Num.FOUR
1 is Num.ONE
Num.TWO is Num.TWO
注意到 Num.FOUR
的值和 Num.ONE
相同,此时前者会被当作后者的别名,遍历时也不会显现
print(Num.FOUR) # 别名
for type_name in Num:
print(type_name) # 没有 Num.FOUR
如果想要将别名输出,可以访问枚举类下的成员变量__members__
for type_name in Num.__members__:
print(type(type_name), type_name)
枚举类型在数据库中存储的通常是类型值,这样不可避免的在编程时需要把从数据库读来的数据转换成枚举类型,这一步可以通过构造函数实现
type_name = Num(1)
print(type(type_name), type_name)
type_name == Num.ONE
继承 Enum 得到的枚举类不限定类型值,甚至可以混用,如果希望值的类型全部统一成数字,可以使用 IntEnum。
from enum import IntEnum
class VIP(IntEnum):
'''QQ钻石会员'''
RED = 1
YELLOW = 2
GREEN = 3
BLACK = 'black' # 会报错
PINK = 5
此外,如果希望类型值彼此互斥,保证唯一性。可以借助 unique 触发器
from enum import Enum, unique
@unique
class Num(Enum):
ONE = 1
TWO = 2
THREE = 3
FOUR = 1 # 会报错
def call(a):
return 'call %s' % a
c = call # 把函数赋值给变量
print(c('me'), type(c))
def get_y():
slope = 2
def one_dim(x): # 函数内部定义一个函数,且接受一个参数
return slope*x + 1
return one_dim # 函数作为参数返回
因为可见性不同,外部不能直接调用函数one_dim
,但是可以通过调用get_y
间接使用one_dim
y = get_y()
y(2)
这里实际上y = one_dim
,y(2) 等价于 one_dim(2), 计算 2 × 2 + 1 = 5
现象二
如果get_y
内部没有给定斜率,我们在外部进行定义,按照链式作用规则,我们依然可以得到相同结果
slope = 2
def get_y():
def one_dim(x):
return slope*x + 1 # 内部没有会向上一级寻找
return one_dim
y = get_y()
y(2)
现象三
如果内外同时定义了斜率,但是值不相同,会发生什么呢?
def get_y():
slope = 2
def one_dim(x):
return slope*x + 1 # 内部没有会向上一级寻找
return one_dim
slope = 4
y = get_y()
y(2)
结果似乎有些奇怪,按理说 y
就是 one_dim
,计算 y(2)
时因为内部没有斜率slope
进而向外部访问。但这里没有访问与y
同级的斜率slope=4
,而是使用了one_dim
定义时同级的斜率slope=2
,这种函数定义和定义时存在的变量(环境变量)彼此绑定在一起的状态就叫做闭包,即闭包 = 函数 + 环境变量,环境变量不能是全局变量
当get_y
返回one_dim
时,返回的不只是函数对象,实际上返回的是闭包,可以通过内部变量__closure__
查看
y.__closure__
# 查看闭包内的环境变量
y.__closure__[0].cell_contents
闭包的意义在于将函数定义时的现场保存了下来,这样在函数调用时可以免受外部干扰,保证运行结果的正确。也正因为是和环境绑定,环境不同闭包也就不同,闭包不能由单一函数决定
def get_y():
slope = 3
def one_dim(x): # 一个闭包
return slope*x + 1
def get_k():
slope = 4
def one_dim(x): # 另一个闭包
return slope*x + 1
闭包的关键在于函数调用外部的环境变量,如果函数内部定义了同名变量,Python 会将其视作局部变量进行访问、返回,不再是闭包。闭包与是否返回值无关
def f1():
a = 10
def f2():
a = 20 # 局部变量,不再是闭包
print(a) # 访问内部的局部变量
print(a) # 访问 f1.a
f2()
print(a) # f2 无法操纵 f1.a,依旧是10
f1()
def f1():
a = 10
def f2():
a = 20 # 没有调用外部环境变量,不是闭包
return f2 # 闭包与返回值无关
f = f1()
print(type(f))
print(f.__closure__) # 不是闭包故为空
def f1():
a = 10
def f2():
print(a) # 调用了外部环境变量,构成闭包
return f2
f = f1()
print(f.__closure__)
闭包存有环境变量,所以当当前步骤的计算需要之前的计算结果,就可以使用闭包
init_pos = 0
def pos(loc): # loc 为环境变量
def go(step):
nonlocal loc # 函数内存在对 loc 的赋值,Python 会解析为局部变量,所以需要声明
new_loc = loc + step
loc = new_loc # 调用外部环境变量,通过闭包积累已经走过的距离
return loc
return go
tour = pos(init_pos)
print(tour(1))
print(tour(2))
print(tour(3))
从另一个角度来看,闭包实现了在外部间接访问原本访问不到的局部变量,提供了一定的灵活性。但是因为存在外部调用的缘故,局部变量的内存空间迟迟得不到回收,因此容易导致内存泄漏