一、闭包
什么是闭包?
记住两个关键字:滞后执行,返回函数。
当函数返回另一个函数时,相关参数和变量都保存在返回的函数中,被称作“闭包”。
由于闭包是调用的时候才执行,属于滞后执行,因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。
思考下面的例子:
大家先想一个普通求和的函数,可以利用可变参数来实现。
如下:
def calc_sum(*args):
ax = 0
for n in args:
ax = ax + n
return ax
调用这个函数可以马上获得求和的结果。
sum = calc_sum(1,2,3,4,5)
print(sum)
#结果为:15
再来看另一个函数lazy_sum:
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
这个函数很奇特,他把求和的过程(也就是sum函数)封装起来,并作为返回值返回。
相当于封装了运算的算法。
如何使用这个函数呢?
可以利用一个变量,将这个函数保存下来,如:
f = lazy_sum(1,3,5,7,9)
此时,f就相当于sum。可以在任何需要的时候,调用f(),完成对1,3,5,7,9的求和。
例如:
print(f())
此时要注意:当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。
测试一下:
f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 9)
print(f1==f2)
#结果为False
以上我们能看出:
我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
简单来说:lazy_sum完成数据装填。 f则负责在合适的时机运行算法。
将数据装填和算法执行分离开来,这就是lazy_sum和sum的本质区别,也是闭包和普通函数的本质区别。
有的同学马上就会提出质疑。一个求和函数,我为什么要把数据和执行分开啊。我可以在任何一个时候装填数据,执行结果,一气呵成呀。
哈哈。
因为这个例子太简单啦。很多复杂的情况下,例如,需要大量初始化工作的复杂算法。每一次都要重新状态数据,难免浪费时间效率和空间效率。装填一次,多次运行,就好多啦。
还有一些情况下,算法的执行,必须缓缓道来。闭包就派上用场了。
思考下面的例子:
利用闭包返回一个计数器函数,每次调用它返回递增整数:
#利用闭包返回一个计数器函数,每次调用它返回递增整数:
# -*- coding: utf-8 -*-
def createCounter():
n = 0
def counter():
nonlocal n #nonlocal允许内函数counter函数访问外函数createcounter的局部变量n,并且可以修改n,如果不修改n的话,可以省略这条语句
n = n + 1
return n
return counter
# 测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
print('测试通过!')
else:
print('测试失败!')
从上例可以看出,每执行一次counterA,就可以返回一个递增的数据。
由此,可以总结出闭包的作用
二、闭包的作用
1.保存外函数的局部变量的状态,在多次调用时,仍然不初始化,有点像C中的静态变量。
2.让函数具备惰性,实现滞后执行,或者分步执行的效果。
# #lazysum就是滞后执行的例子,只有真正调用的时候才开始执行。
让函数分步执行的例子,可参考下面:
def step(s):
print(s)
def step2():
print('step2')
return step2
sfunc = step('step1')
print('other task1')
print('other task2')
sfunc()
上面例子的结果为:
step1
other task1
other task2
step2
从上例可以看出,函数的step1和step2分步执行了。同时,也要注意到,step函数作为闭包函数,不但可以包含滞后运行的算法,也可以包含立即执行的算法,比如print(s)
三、闭包的应用场景
思考下面的代码:
def test(a,b,c):
print( a*b+c)
test(1,1,2)
test(1,1,3)
test(1,1,4)
test(1,1,5)
test(1,1,6)
a,b 是固定的,只是c 会变动,如何减少a,b 参数的传递, 开发的信仰咱们忘了吗 , 不写重复的代码 。
可以将a和b两个固定参数,封装起来形成闭包,一次装填,反复使用。
def test(a,b):
def test_in(c):
print( a*b+c)
return test_in
num=test(1,1) # 把test_in 引用交给了num
num(2) # 执行 内嵌函数
num(3) # 达到了减少 参数的传递,
num(4)
num(5)
如果不想用原始的ab值,也可以重新创建新的引用
num_02=test(2,2) # 如果不想用上边的 a,b 值 可以用重新创建新的引用 num_02
num_02(2)
num_02(3)
num_02(4)
num_02(5)
学会了就点个赞吧。我是瑞哥。