为什么不是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函数返回后它将保持活动状态。

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

  1. 访问本地封闭范围的变量,
  2. 在这个范围之外执行时,

那么他们不是关闭的。

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

 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

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

我有一种情况,我需要一个单独的,但持久的名称空间。 我使用类。 我不这样做。 隔离但持久的名称是关闭。

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

这是关闭的一个例子,以及如何使用它。

Python 2没有关闭 – 它有类似闭包的解决方法。

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

在Python 3中,支持更加明确 – 简洁:

 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关键字将内部函数绑定到明确提到的外部变量,实际上包含它。 因此更明确地说是“关闭”。

我想提供另一个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中,如果在内部作用域中有一个赋值给同名的变量,那么就会在内部作用域中创建一个新的引用。 除非你用var关键字明确地声明了一个,否则JS不是这样。