代码之家  ›  专栏  ›  技术社区  ›  Srikar Appalaraju Tonetel

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

  •  222
  • Srikar Appalaraju Tonetel  · 技术社区  · 14 年前

    nested functions 而不是 closures

    嵌套函数不是闭包,因为外部世界不使用它们吗?

    更新: 我读到关于闭包的文章,它让我想到了关于Python的这个概念。我在下面的评论中找到了一篇有人提到的文章,但是我不能完全理解那篇文章中的解释,所以这就是我问这个问题的原因。

    7 回复  |  直到 10 年前
        1
  •  381
  •   aaronasterling    14 年前

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

    def make_printer(msg):
        def printer():
            print msg
        return printer
    
    printer = make_printer('Foo!')
    printer()
    

    什么时候? make_printer 调用时,将一个新帧放在堆栈上,其中包含 printer 作为常数的函数和 msg 作为本地人。然后创建并返回函数。因为函数 引用 味精 制版打印机 函数已返回。

    如果你的嵌套函数

    1. 访问封闭作用域的本地变量,
    2. 当它们在该范围之外执行时,

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

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

    打印机 是创建的,因此没有引用 味精 外部到 打印机 以后需要保养 制版打印机 返回。 只是函数的正常局部变量 打印机

        2
  •  100
  •   Community CDub    8 年前

    这个问题已经有人回答了 aaronasterling

    然而,有些人可能对变量如何存储在引擎盖下感兴趣。

    在谈到片段之前:

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

    • 如果有另一个内部级别使用自由变量-- 全部的

    • 功能属性 func_closure 在里面 python<3.X或 __closure__

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

    示例:闭包属性,但内部没有内容,因为没有自由变量。

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

    注: FREE VARIABLE 必须创建闭包。

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

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

    所有Python函数都有一个closure属性,因此让我们检查与closure函数相关联的封闭变量。

    func_闭包 为了这个功能 printer

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

    这个 closure 属性返回单元格对象的元组,该元组包含在封闭范围中定义的变量的详细信息。

    func_闭包中的第一个元素,它可以是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() ,它访问存储在 单元格内容

    我将再次解释使用上述代码片段的一些更改:

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

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

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

    >>> 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_闭包 属性是闭包的元组 ,我们可以显式地引用它们及其内容--单元格具有“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'
    >>>
    
        3
  •  70
  •   jmcker    5 年前

    Python有一个 虚弱的

    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
    

    闭包非常优雅,因为它给这样编写的函数提供了拥有“内部内存”的能力。从Python2.7开始,这是不可能的。如果你试着

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

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

    function decorator pattern Python为其提供语法糖。

    更新

    正如已经指出的,有很多方法可以处理python的范围限制,我将展示一些方法。

    使用 global 关键字(一般不推荐)。

    2。 nonlocal 关键字(由@unutbu和@leewz建议)

    三。 定义一个简单的可修改类 Object

    class Object(object):
        pass
    

    并创建 Object scope initCounter 存储变量

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

    自从 scope 实际上只是一个引用,对其字段执行的操作不会真正修改 本身,所以不会出现错误。

    四。 正如@unutbu指出的,另一种方法是将每个变量定义为一个数组( x = [0] )修改它的第一个元素( x[0] += 1 ). 同样不会出现错误,因为 x

    5个。 正如@raxacoricofallapatorius所建议的,你可以 财产 counter

    def initCounter ():
    
        def counter():
            counter.x += 1
            print counter.x
    
        counter.x = 0
        return counter
    
        4
  •  16
  •   Lee Benson    8 年前

    Python2没有闭包-它有解决方案 相似的 关闭。

    已经给出的答案中有很多例子——将变量复制到内部函数、修改内部函数上的对象等等。

    def closure():
        count = 0
        def inner():
            nonlocal count
            count += 1
            print(count)
        return inner
    

    用法:

    start = closure()
    start() # prints 1
    start() # prints 2
    start() # prints 3
    

    这个 nonlocal 关键字将内部函数绑定到显式提到的外部变量,实际上是将其括起来。因此更明确地说是“闭包”。

        5
  •  9
  •   fp_mora    10 年前

    我有一个情况,我需要一个单独但持久的名字空间。 我上过课。否则我不会。 隔离但持久的名称是闭包。

    >>> class f2:
    ...     def __init__(self):
    ...         self.a = 0
    ...     def __call__(self, arg):
    ...         self.a += arg
    ...         return(self.a)
    ...
    >>> f=f2()
    >>> f(2)
    2
    >>> f(2)
    4
    >>> f(4)
    8
    >>> f(8)
    16
    
    # **OR**
    >>> f=f2() # **re-initialize**
    >>> f(f(f(f(2)))) # **nested**
    16
    
    # handy in list comprehensions to accumulate values
    >>> [f(i) for f in [f2()] for i in [2,2,4,8]][-1] 
    16
    
        6
  •  5
  •   Rob Bednark Bohdan    10 年前
    def nested1(num1): 
        print "nested1 has",num1
        def nested2(num2):
            print "nested2 has",num2,"and it can reach to",num1
            return num1+num2    #num1 referenced for reading here
        return nested2
    

    In [17]: my_func=nested1(8)
    nested1 has 8
    
    In [21]: my_func(5)
    nested2 has 5 and it can reach to 8
    Out[21]: 13
    

    这是一个什么是闭包以及如何使用闭包的示例。

        7
  •  0
  •   forumulator    7 年前

    我想提供python和JS示例之间的另一个简单比较,如果这有助于使事情更清楚的话。

    JS公司:

    function make () {
      var cl = 1;
      function gett () {
        console.log(cl);
      }
      function sett (val) {
        cl = val;
      }
      return [gett, sett]
    }
    

    执行:

    a = make(); g = a[0]; s = a[1];
    s(2); g(); // 2
    s(3); g(); // 3
    

    蟒蛇:

    def make (): 
      cl = 1
      def gett ():
        print(cl);
      def sett (val):
        cl = val
      return gett, sett
    

    执行:

    g, s = make()
    g() #1
    s(2); g() #1
    s(3); g() #1
    

    原因: 如上所述,在python中,如果在内部作用域中有对同名变量的赋值,则会在内部作用域中创建一个新引用。对于JS则不是这样,除非您显式地用 var 关键字。