我在Python中见过并使用过嵌套函数,它们与闭包的定义相匹配。那么为什么它们被称为“嵌套函数”而不是“闭包”呢?

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

更新:我正在阅读关于闭包的文章,它让我想到了关于Python的这个概念。我搜索了下面评论中有人提到的文章,但我不能完全理解文章中的解释,所以我才问这个问题。


当前回答

我遇到了这样一种情况,需要一个单独但持久的名称空间。 我使用课堂。否则我不会。 隔离但持久的名称是闭包。

>>> 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

其他回答

这个问题aaronasterling已经回答过了

然而,有些人可能对变量在底层是如何存储的感兴趣。

在开始之前:

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

如果一个函数不使用自由变量,它就不能形成闭包。 如果存在另一个使用自由变量的内部关卡——所有之前的关卡都会保存词汇环境(例如最后的例子) python中的函数属性func_closure < 3。3. python >中的__closure__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函数都有一个闭包属性,所以让我们检查一下与闭包函数相关的封闭变量。

下面是函数打印机的func_closure属性

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

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

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
 >>>

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

现在我将解释另一个不同的片段,以清除所有的自由变量与闭包:

>>> 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是自由变量

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

我遇到了这样一种情况,需要一个单独但持久的名称空间。 我使用课堂。否则我不会。 隔离但持久的名称是闭包。

>>> 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

我想提供另一个简单的比较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

Python:

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关键字声明一个。

Python 2没有闭包——它有类似闭包的变通方法。

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

在Python 3中,支持更加显式和简洁:

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

用法:

start = closure()
another = closure() # another instance, with a different stack

start() # prints 1
start() # prints 2

another() # print 1

start() # prints 3

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

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

这是一个关于闭包是什么以及如何使用它的例子。