为什么不是称为闭包的python嵌套函数?

我已经看到并在Python中使用嵌套函数,它们匹配闭包的定义。 那么为什么他们调用nested functions而不是closures

嵌套函数是不是因为它们不被外部世界使用而关闭?

更新:我正在阅读关于闭包的知识,这让我想起了关于Python的这个概念。 我在下面的评论中搜索并找到了某人提到的文章,但是我无法完全理解那篇文章中的解释,所以这就是我为什么要问这个问题的原因。


当函数可以从已经完成执行的封闭作用域访问局部变量时,会发生闭包。

def make_printer(msg):
    def printer():
        print msg
    return printer

printer = make_printer('Foo!')
printer()

make_printer ,会在堆栈中放置一个新框架,并将printer函数的编译代码作为常量,将msg的值作为本地值。 然后它创建并返回该函数。 由于函数printer引用了msg变量,因此在make_printer函数返回后它会保持活动状态。

所以,如果你的嵌套函数不

  • 访问包含作用域的局部变量,
  • 当它们在该范围之外执行时这样做,
  • 那么他们不是关闭。

    这是一个嵌套函数的例子,它不是闭包。

    def make_printer(msg):
        def printer(msg=msg):
            print msg
        return printer
    
    printer = make_printer("Foo!")
    printer()  #Output: Foo!
    

    在这里,我们将该值绑定到参数的默认值。 这发生在创建功能printer时,因此在make_printer返回后, make_printer维护对printer外部msg值的引用。 在这种情况下, msg只是功能printer的常规局部变量。


    这个问题已经由 aaronasterling 回答了

    但是,有人可能对如何将变量存储在引擎盖下感兴趣。

    在加入片段之前:

    闭包是从其环境中继承变量的函数。 将函数回调作为参数传递给另一个将执行I / O的函数时,稍后会调用此回调函数,并且此函数会 - 几乎神奇地记住它声明的上下文以及所有可用变量在这方面。

  • 如果一个函数不使用自由变量,它不会形成闭包。

  • 如果有另一个使用自由变量的内部级别 - 所有以前的级别保存词汇环境(例如最后)

  • 函数属性func_closurepython <3.X或__closure__在python> 3.X中保存自由变量。

  • python中的每个函数都有这个闭包属性,但是如果没有自由变量,它不会保存任何内容。

  • 例如:关闭属性,但没有内容,因为没有自由变量。

    >>> def foo():
    ...     def fii():
    ...         pass
    ...     return fii
    ...
    >>> f = foo()
    >>> f.func_closure
    >>> 'func_closure' in dir(f)
    True
    >>>
    

    注意:自由变量必须创建关闭。

    我将使用与上面相同的片段来解释:

    >>> def make_printer(msg):
    ...     def printer():
    ...         print msg
    ...     return printer
    ...
    >>> printer = make_printer('Foo!')
    >>> printer()  #Output: Foo!
    

    所有的Python函数都有一个闭包属性,所以让我们来看看与闭包函数相关的封闭变量。

    这是功能printer的属性func_closure

    >>> 'func_closure' in dir(printer)
    True
    >>> printer.func_closure
    (<cell at 0x108154c90: str object at 0x108151de0>,)
    >>>
    

    closure属性返回一个元组对象,它包含在封闭范围中定义的变量的细节。

    func_closure中的第一个元素可以是None或者包含函数自由变量绑定的单元的元组,它是只读的。

    >>> dir(printer.func_closure[0])
    ['__class__', '__cmp__', '__delattr__', '__doc__', '__format__', '__getattribute__',
     '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
     '__setattr__',  '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
    >>>
    

    在上面的输出中,您可以看到cell_contents ,让我们看看它存储的内容:

    >>> printer.func_closure[0].cell_contents
    'Foo!'    
    >>> type(printer.func_closure[0].cell_contents)
    <type 'str'>
    >>>
    

    所以,当我们调用函数printer() ,它会访问存储在cell_contents的值。 这就是我们如何得到输出为'Foo!'

    再次,我会解释一下使用上面的代码片段:

     >>> def make_printer(msg):
     ...     def printer():
     ...         pass
     ...     return printer
     ...
     >>> printer = make_printer('Foo!')
     >>> printer.func_closure
     >>>
    

    在上面的代码片段中,我不打印打印机函数中的msg,所以它不会创建任何自由变量。 由于没有可用变量,因此闭包内不会有内容。 这正是我们上面看到的。

    现在我将解释另一个不同的片段来清除Free Variable Closure所有Free Variable

    >>> def outer(x):
    ...     def intermediate(y):
    ...         free = 'free'
    ...         def inner(z):
    ...             return '%s %s %s %s' %  (x, y, free, z)
    ...         return inner
    ...     return intermediate
    ...
    >>> outer('I')('am')('variable')
    'I am free variable'
    >>>
    >>> inter = outer('I')
    >>> inter.func_closure
    (<cell at 0x10c989130: str object at 0x10c831b98>,)
    >>> inter.func_closure[0].cell_contents
    'I'
    >>> inn = inter('am')
    

    因此,我们看到func_closure属性是闭包单元的元组,我们可以明确地引用它们及其内容 - 单元具有属性“cell_contents”

    >>> inn.func_closure
    (<cell at 0x10c9807c0: str object at 0x10c9b0990>, 
     <cell at 0x10c980f68: str object at   0x10c9eaf30>, 
     <cell at 0x10c989130: str object at 0x10c831b98>)
    >>> for i in inn.func_closure:
    ...     print i.cell_contents
    ...
    free
    am 
    I
    >>>
    

    在这里,当我们调用inn ,它会引用所有保存的自由变量,所以我们得到的I am free variable

    >>> inn('variable')
    'I am free variable'
    >>>
    

    Python对闭包的支持很弱。 看看我的意思是使用JavaScript使用闭包的下面的例子:

    function initCounter(){
        var x = 0;
        function counter  () {
            x += 1;
            console.log(x);
        };
        return counter;
    }
    
    count = initCounter();
    
    count(); //Prints 1
    count(); //Prints 2
    count(); //Prints 3
    

    关闭是非常优雅的,因为它赋予这样写的功能具有“内部存储器”的能力。 从Python 2.7开始,这是不可能的。 如果你试试

    def initCounter():
        x = 0;
        def counter ():
            x += 1 ##Error, x not defined
            print x
        return counter
    
    count = initCounter();
    
    count(); ##Error
    count();
    count();
    

    你会得到一个错误,说x没有被定义。 但是如果其他人已经证明你可以打印它,那怎么可能呢? 这是因为它如何管理函数变量作用域。 虽然内部函数可以读取外部函数的变量,但它不能写入它们。

    这真的很遗憾。 但是只用read-only闭包,你至少可以实现Python为其提供语法糖的函数装饰器模式。

    更新

    正如它已经指出的,有办法来处理python的范围限制,我会公开一些。

    1.使用global关键字(通常不推荐)。

    2.定义一个简单的可修改类Object

    class Object(object):
        pass
    

    并在initCounter创建一个Object scope来存储变量

    def initCounter ():
        scope = Object()
        scope.x = 0
        def counter():
            scope.x += 1
            print scope.x
    
        return counter
    

    由于scope实际上只是一个参考,因此使用其域的操作并不真正修改scope本身,因此不会出现错误。

    3.另一种方式,如@unutbu指出的那样,将每个变量定义为一个数组( x = [0] )并修改它的第一个元素( x[0] += 1 )。 再次没有出现错误,因为x本身没有被修改。

    4.按照@raxacoricofallapatorius的建议,你可以使x成为counter一个属性

    def initCounter ():
    
        def counter():
            counter.x += 1
            print counter.x
    
        counter.x = 0
        return counter
    
    链接地址: http://www.djcxy.com/p/9047.html

    上一篇: Why aren't python nested functions called closures?

    下一篇: Singleton by Jon Skeet clarification