Python 闭包相关知识
闭包字面意思,是封闭的东西,为了保证数据的安全。
用案例解释闭包:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。
比如大众推出了一款新车:小白轿车。
第一天价格为:100000元,平均收盘价:100000元
第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元
第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元
…………
实现以上需求,方案一:不能保证数据安全
l1 = [] # 全局变量,数据不安全,容易被误操作改变。
li = [] # 相似的变量
def make_averager(new_value):
l1.append(new_value)
total = sum(l1)
return total / len(l1)
print(make_averager(100000)) # 100000.0
print(make_averager(110000)) # 105000.0
l1.append(34556) # 本来要对li进行操作,但是不小心追加错了,造成最终结果出错。
print(make_averager(120000)) # 83364.0
方案二:使用闭包,外部无法对数据修改,只能以特定的渠道进行调用。
def make_averager():
l1 = [] # 将l1放置在函数内,变成了局部变量,
def averager(new_value): # l1在这里被嵌套函数averager调用了,
l1.append(new_value) # 列表l1和函数averager之间产生了一个绑定关系,
return sum(l1) / len(l1)
print(locals())
return averager
avg = make_averager() # 接下来嵌套函数averager被赋值给了avg变量,
print(avg(100000)) # 所以l1虽然在全局变量列表中已经消失,不可调用,但却真实存在,
# l1.append(54464) # 这行代码调用将出错
print(avg(110000)) # 只有通过avg变量调用才能对l1进行改变,
print(avg(90000)) # 这个现象就是闭包。
print(globals())
-
闭包的定义:
- 闭包是嵌套在函数中的函数。
- 内层函数对外层函数的变量(非全局变量)的引用(使用),就会形成闭包。
-
闭包的现象:
- 被引用的非全局变量也称作*变量,这个*变量会与内层函数产生一个绑定关系,*变量不会在内存中消失。
-
闭包的作用:保存局部信息不被销毁,保证数据的安全性。
-
闭包的应用:
- 可以保存一些非全局变量但是不易被销毁、改变的数据。
- 装饰器。
-
判断是否有闭包:
-
使用代码判断是否存在闭包现象,只需要判断是否存在有“*变量”即可,判断代码为:
def func(a, b): # 这段代码虽然看上去变量a,b为全局变量, def inner(): # 但是传入函数内将生成一个局部变量a,b print(a) # 函数内部的a,b变量和外面的并不是同一个 print(b) return inner a = 2 b = 3 ret = func(a, b) # 所以这条命令之后将形成一个闭包现象 print(ret.__code__.co_freevars) # 以这条指令判断是否存在“*变量” >>>('a', 'b') # 运行结果有a,b两个变量属于*变量,可以断定上面代码存在闭包现象。
-
-
查看函数相关的其他参数:
-
有一些函数的属性是可以获取到此函数是否拥有*变量的,如果此函数拥有*变量,那么就可以侧面证明其是否是闭包函数了,以下还有一些参数,仅供了解:
def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager avg = make_averager() # 函数名.__code__.co_freevars 查看函数的*变量 print(avg.__code__.co_freevars) # ('series',) # 函数名.__code__.co_varnames 查看函数的局部变量 print(avg.__code__.co_varnames) # ('new_value', 'total') # 函数名.__closure__ 获取具体的*变量对象,也就是cell对象。 # (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,) # cell_contents *变量具体的值 print(avg.__closure__[0].cell_contents) # []
-