Python中的闭包是怎么回事儿?五分钟看懂。

一、闭包

什么是闭包?

记住两个关键字:滞后执行,返回函数

当函数返回另一个函数时,相关参数和变量都保存在返回的函数中,被称作“闭包”。

由于闭包是调用的时候才执行,属于滞后执行,因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。

思考下面的例子:

大家先想一个普通求和的函数,可以利用可变参数来实现。

如下:

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)

学会了就点个赞吧。我是瑞哥。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎风工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值