• 自学Python 35 闭包:函数和利用引用环境组合而成的实体


    Python 闭包



      在计算机编程应用中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以还有另一种说法,认为闭包是由函数和其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生个同的实例。


    一、什么是闭包

      根据字面意思,可以形象地把闭包理解为一个封闭的包裹,这个包裹就是一个函数,当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量,自由变量可以随着包裹到处游荡。当然还得有个前提,这个包裹是被创建出来的。在Python语言中,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫作闭包。你在调用函数A的时候,传递的参数就是一个自由变量。例如在下面的实例代码中演示了生成一个闭包的具体过程

    def func (name):
        def inner_func (date):
            print ('name: ',name, 'date:', date)
        return inner_func
    bb= func('暑假')
    bb (71)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      在上述实例代码中,当调用函数 func(的时候就产生了一个闭包:inner_func(),并且该闭包拥有自由变量“name”。这表示当函数func()的生命周期结束之后,变量name会依然存在,因为它被闭包引用了,所以不会被回收。执行后会输
    在这里插入图片描述
      注意:闭包并不是Python语言所特有的概念,所有把函数作为“一等公民”的语言均有闭包这一概念。不过像Java这样以 class为“一等公民”的语言中也可以使用闭包,只是它得用类或接口来实现。

      另外,如果从表现形式上来讲解 Python 中的闭包,表示如果在一个内部函数里,对外部作用域(但个是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包。这种解释非常容易理解,不像其他定义那样有一堆陌生名词,不适合初学者。
      注意:闭包和类的异同
      基于前面内容的介绍,相信读者已经发现闭包和类有点相似,相似点在于它们都提供了对数据的封装。不同的是闭包本身就是个方法。和类一样,我们在编程时经常会把通用的东西抽象成类(当然,还有对现实世界—业务的建模),以复用通用的功能。闭包也是一样,当我们需要函数粒度的抽象时,闭包就是一个很好的选择。在这点上闭包可以被理解为一个只读对象,你可以给它传递一个属性,但它只能提供给你一个执行的接口。因此在程序中我们经常需要这样的一个函数对象——闭包来帮我们完成一个通用的功能,比如之前讲到的装饰器。

    二、闭包和嵌套函数

      在Python语言中,闭包是指将组成函数的语句和这些语句的执行环境打包到一起所得到的对象。当使用嵌套函数(函数中定义函数)时,闭包将捕获内部函数执行所需的整个环境。此外,嵌套函数可以使用被嵌套函数中的任何变量,就像普通函数可以引用全局变量一样,而不需要通过参数引入。
      例如在下面的实例代码中,演示了嵌套函数可以使用被嵌套函数中的任何变量的过程。

    x=14        #定义全局变量x
    def foo() :     #定义嵌套函数的外层函数foo()
        x=50
        def bar() :     #定义一个变量x的初始值是3
            print('x的值是:%d' %x)      #定义嵌套的内层函数bar()
        print('暑假的计划是学%d' %x,'天')       #引用的变量X
        bar ()      #调用嵌套的内层函数bar()
    if __name__ == '__main__':
            foo()       #调用嵌套函数的外层函数foo()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      在上述实例代码中定义了一个全局变量x,在嵌套函数的外层函数foo中也定义了一个变量x:在嵌套的内层函数bar()中引用的变量x应该是foo()中定义的x。因为嵌套函数可以直接引用其外层的函数中定义的变量x的值并输出,所以输出的值为50,而不是全局变量x的值14。执行后会输出:
    在这里插入图片描述

    三、使用闭包记录函数被调用的次数

      为了深入理解Python闭包的知识,下面举一个内外嵌套函数调用的例子。我们可以把这个实例看作是统计
    个函数调用次数的函数。可以将count[0]看作是个计数器, 每执行一次函数hello(), count[0] 的值就加1。具体实现代码如下所示。

    def hellocounter (name) :
        count=[0]
        def counter () :
            count[0]+=1
            print ('Hello, ',name, ',',str (count[0])+' access!')
        return counter
    hello = hellocounter ('暑假')
    hello()
    hello()
    hello()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      在上述实例代码中,也许有的读者会提出疑问:为什么不直接写count,而用一个列表实现呢?这其实是Python2的一个bug,如果不用列表的话,会报如下所示的错误。
    UnboundLocalError: local variable 'count' referenced before assignment.
      上述错误的意思是说变量count没有定义就直接引用了,于是在Python 3中引入了一个关键字nonlocal,这个关键字的功能是告诉Python程序,这个count变量是在外部定义的,然后Python就会去外层函数查找变量count,然后就找到了count=0这个定 义和赋值,这样程序就能正常执行了。执行后会输出:
    在这里插入图片描述

  • 相关阅读:
    如何将vscode和Linux远程链接:
    设计模式之观察者模式
    Git小书系列笔记
    C++基础入门丨5. 数组——一维数组和二维数组
    STM32 + CT1711超级准的人体测温方案
    Java基础之《netty(2)—IO模型、BIO介绍、NIO介绍》
    python读取yaml文件内容
    PyQt5学习笔记--摄像头实时视频展示、多线程处理、视频编解码
    如何骚操作通过科目一斩获高分,python带你了解~
    【课程作业】最优化理论与方法:第一次作业
  • 原文地址:https://blog.csdn.net/weixin_46066007/article/details/125434829