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


    一、闭包

    什么是闭包?

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

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

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

    思考下面的例子:

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

    如下:

    1. def calc_sum(*args):
    2. ax = 0
    3. for n in args:
    4. ax = ax + n
    5. return ax

    调用这个函数可以马上获得求和的结果。

    1. sum = calc_sum(1,2,3,4,5)
    2. print(sum)
    3. #结果为:15

    再来看另一个函数lazy_sum:

    1. def lazy_sum(*args):
    2. def sum():
    3. ax = 0
    4. for n in args:
    5. ax = ax + n
    6. return ax
    7. return sum

    这个函数很奇特,他把求和的过程(也就是sum函数)封装起来,并作为返回值返回。

    相当于封装了运算的算法。

    如何使用这个函数呢?

    可以利用一个变量,将这个函数保存下来,如:

    f = lazy_sum(1,3,5,7,9)

    此时,f就相当于sum。可以在任何需要的时候,调用f(),完成对1,3,5,7,9的求和。

    例如:

    print(f())

    此时要注意:当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。

    测试一下:
     

    1. f1 = lazy_sum(1, 3, 5, 7, 9)
    2. f2 = lazy_sum(1, 3, 5, 7, 9)
    3. print(f1==f2)
    4. #结果为False

    以上我们能看出:

    我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

    简单来说:lazy_sum完成数据装填。 f则负责在合适的时机运行算法。

    将数据装填和算法执行分离开来,这就是lazy_sum和sum的本质区别,也是闭包和普通函数的本质区别。

    有的同学马上就会提出质疑。一个求和函数,我为什么要把数据和执行分开啊。我可以在任何一个时候装填数据,执行结果,一气呵成呀。

    哈哈。

    因为这个例子太简单啦。很多复杂的情况下,例如,需要大量初始化工作的复杂算法。每一次都要重新状态数据,难免浪费时间效率和空间效率。装填一次,多次运行,就好多啦。

    还有一些情况下,算法的执行,必须缓缓道来。闭包就派上用场了。

    思考下面的例子:

    利用闭包返回一个计数器函数,每次调用它返回递增整数

    1. #利用闭包返回一个计数器函数,每次调用它返回递增整数:
    2. # -*- coding: utf-8 -*-
    3. def createCounter():
    4. n = 0
    5. def counter():
    6. nonlocal n #nonlocal允许内函数counter函数访问外函数createcounter的局部变量n,并且可以修改n,如果不修改n的话,可以省略这条语句
    7. n = n + 1
    8. return n
    9. return counter
    10. # 测试:
    11. counterA = createCounter()
    12. print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
    13. counterB = createCounter()
    14. if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
    15. print('测试通过!')
    16. else:
    17. print('测试失败!')

    从上例可以看出,每执行一次counterA,就可以返回一个递增的数据。

    由此,可以总结出闭包的作用

    二、闭包的作用

    1.保存外函数的局部变量的状态,在多次调用时,仍然不初始化,有点像C中的静态变量。

    2.让函数具备惰性,实现滞后执行,或者分步执行的效果。

    # #lazysum就是滞后执行的例子,只有真正调用的时候才开始执行。

    让函数分步执行的例子,可参考下面:

    1. def step(s):
    2. print(s)
    3. def step2():
    4. print('step2')
    5. return step2
    6. sfunc = step('step1')
    7. print('other task1')
    8. print('other task2')
    9. sfunc()

    上面例子的结果为:

    1. step1
    2. other task1
    3. other task2
    4. step2

    从上例可以看出,函数的step1和step2分步执行了。同时,也要注意到,step函数作为闭包函数,不但可以包含滞后运行的算法,也可以包含立即执行的算法,比如print(s)

    三、闭包的应用场景

    思考下面的代码:

    1. def test(a,b,c):
    2. print( a*b+c)
    3. test(1,1,2)
    4. test(1,1,3)
    5. test(1,1,4)
    6. test(1,1,5)
    7. test(1,1,6)

    a,b 是固定的,只是c 会变动,如何减少a,b 参数的传递, 开发的信仰咱们忘了吗 , 不写重复的代码 。

    可以将a和b两个固定参数,封装起来形成闭包,一次装填,反复使用。

    1. def test(a,b):
    2. def test_in(c):
    3. print( a*b+c)
    4. return test_in
    5. num=test(1,1) # 把test_in 引用交给了num
    6. num(2) # 执行 内嵌函数
    7. num(3) # 达到了减少 参数的传递,
    8. num(4)
    9. num(5)

    如果不想用原始的ab值,也可以重新创建新的引用

    1. num_02=test(2,2) # 如果不想用上边的 a,b 值 可以用重新创建新的引用 num_02
    2. num_02(2)
    3. num_02(3)
    4. num_02(4)
    5. num_02(5)

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

  • 相关阅读:
    企业电子招投标采购系统——功能模块&功能描述+数字化采购管理 采购招投标
    腾讯云毫无原因封锁思维导图在线工具网站
    c++语言--面试题
    数学建模—灰色关联分析
    promise加强
    [VM trunk ports]opensatck VM 单网卡,多VLAN配置
    Docker部署ChatGLM3、One API、FastGPT
    全网echarts案例资源大总结和echarts的高效使用技巧(细节版)
    Vue介绍&如何安装vue&Vue生命周期钩子&MVVM
    c语言按位与,按位或,按位异或,按位取反
  • 原文地址:https://blog.csdn.net/oJinGangZuan/article/details/127485992