• Python中的闭包


    闭包是什么

    使用像javascript和python这样支持面向对象范式的语言进行编程时,都会涉及到闭包的概念以及闭包的使用。我们今天就从这两个方面来讨论一下闭包(本文仅针对python3):

    首先是维基百科中关于闭包的概念:
    在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。闭包可以用来在一个函数与一组“私有”变量之间创建关联关系。在给定函数被多次调用的过程中,这些私有变量能够保持其持久性。

    根据这句话,其实我们自己就可以总结出在python语言中形成闭包的三个条件,缺一不可:
    1)必须有一个内嵌函数(函数里定义的函数)——这对应函数之间的嵌套
    2)内嵌函数必须引用一个定义在闭合范围内(外部函数里)的变量——内部函数引用外部变量
    3)外部函数必须返回内嵌函数——必须返回那个内部函数

    前两个条件我们比较好理解,那什么会有第三条规定呢?其实闭包一词指的就是上文中提到的那个“内部的函数”,我们下面就会发现,只有那个内部函数才有所谓的__closure__属性。

    我们首先来创造一个闭包:

    def funx():
        x=5
        def funy():
            nonlocal x
            x+=1
            return x
        return funy
    

    我们根据上面的三准则创造了一个函数,其中的funy就是所谓的闭包,而funy内部所引用过的x就是所谓的闭包变量。

    闭包有什么用

    我们在交互式模式下,对上面的函数进行简单的测试:

    >>> a=funx()
    >>> a()
    6
    >>> a()
    7
    >>> a()
    8
    >>> a()
    9
    >>> x
    Traceback (most recent call last):
      File "<pyshell#19>", line 1, in <module>
        x
    NameError: name 'x' is not defined
    >>> 
    

    我们会发现,funx中的x变量原本仅仅是funx的一个局部变量。但是形成了闭包之后,它的行为就好像是一个全局变量一样。但是最后的错误说明x并不是一个全局变量。其实这就是闭包的一个十分浅显的作用,形成闭包之后,闭包变量能够随着闭包函数的调用而实时更新,就好像是一个全局变量那样。(注意我们上面的a=funx(),a实际上应该是funy,所以a称为闭包)

    进一步探究

    我们能否找出点证据证明我们对于闭包的猜想呢?很简单,我们可以尝试下面的操作:

    >>> a.__closure__
    (<cell at 0x0000002F346FB408: int object at 0x00000000667D02D0>,)
    >>> type(a.__closure__)
    <class 'tuple'>
    >>> type(a.__closure__[0])
    <class 'cell'>
    >>> a.__closure__[0].cell_contents
    9
    >>> a()
    10
    >>> a.__closure__[0].cell_contents
    10
    >>> def test():pass
    
    >>> test.__closure__==None
    True
    >>> 
    

    这样我们就明白了,形成闭包之后,闭包函数会获得一个非空的__closure__属性(对比我们最后的函数test,test是一个不具备闭包的函数,它的__closure__属性是None),这个属性是一个元组。元组里面的对象为cell对象,而访问cell对象的cell_contents属性则可以得到闭包变量的当前值(即上一次调用之后的值)。而随着闭包的继续调用,变量会再次更新。所以可见,一旦形成闭包之后,python确实会将__closure__和闭包函数绑定作为储存闭包变量的场所。

  • 相关阅读:
    js小知识
    elasticsearch查询与sql对应关系
    svnkit 异常:Exception in thread "main" org.tmatesoft.svn.core.SVNException: svn: E200030: SQLite error
    spring中引入多个quertz 注意事项
    ajax跨域
    MetaException(message:For direct MetaStore DB connections, we don't support retries at the client level.)
    center os7 安装mysql
    Center OS 7
    解决js ajax跨越请求 Access control allow origin 异常
    gps位置坐标转百度坐标
  • 原文地址:https://www.cnblogs.com/Khannia/p/6220384.html
Copyright © 2020-2023  润新知