你能解释闭包(与Python有关)吗?

我一直在阅读很多关于封闭的内容,我想我理解他们,但是没有让自己和其他人蒙上阴影,我希望有人能够尽可能简洁清楚地解释封闭。 我正在寻找一个简单的解释,可能会帮助我了解我想要使用它们的位置和原因。

closuresclosures

对象是附有方法的数据,闭包是附有数据的函数。

def make_counter(): i = 0 def counter(): # counter() is a closure nonlocal i i += 1 return i return counter c1 = make_counter() c2 = make_counter() print (c1(), c1(), c2(), c2()) # -> 1 2 1 2 

很简单:从一个包含范围引用variables的函数,可能在stream程控制已经离开该范围之后。 最后一点非常有用:

 >>> def makeConstantAdder(x): ... constant = x ... def adder(y): ... return y + constant ... return adder ... >>> f = makeConstantAdder(12) >>> f(3) 15 >>> g = makeConstantAdder(4) >>> g(3) 7 

请注意,12和4分别在f和g内部“消失”,这个特征使f和g正确closures。

我喜欢这个简洁而简洁的定义 :

一个可以引用不再活动的环境的函数。

我会补充

闭包允许您将variables绑定到函数中, 而不用将它们作为parameter passing

接受参数的装饰器是闭包的常见用法。 闭包是这种“function工厂”的常见实现机制。 当运行时数据修改策略时,我经常select在策略模式中使用闭包。

在允许匿名块定义的语言中,例如Ruby,C#闭包可以用来实现新的控制结构。 缺less匿名块是Python中闭包的限制之一 。

说实话,我完全理解封闭,除非我从来不清楚“封闭”究竟是什么东西,封闭是什么东西。 我build议你放弃寻找术语背后的逻辑。

无论如何,这是我的解释:

 def foo(): x = 3 def bar(): print x x = 5 return bar bar = foo() bar() # print 5 

这里的一个主要思想是,从foo返回的函数对象保留了一个钩子到本地var'x',即使'x'已经超出了范围,应该是不存在的。 这个钩子是var的本身,而不仅仅是var在当时的值,所以当调用bar时,它会打印5,而不是3。

还要清楚的是,Python 2.x有限的closures:我不能修改“栏”内的'x',因为写'x = bla'会在栏中声明一个本地'x',而不是指定'foo' 。 这是Python的赋值=声明的副作用。 为了解决这个问题,Python 3.0引入了nonlocal关键字:

 def foo(): x = 3 def bar(): print x def ack(): nonlocal x x = 7 x = 5 return (bar, ack) bar, ack = foo() ack() # modify x of the call to foo bar() # print 7 

我从来没有听说过在同一个上下文中使用事务来解释什么是闭包,这里真的没有任何事务语义。

它被称为闭包,因为它closures了外部variables(常量) – 即,它不仅仅是一个函数,而是一个创build函数的环境的shell。

在下面的例子中,改变x之后调用闭包g也会改变g中的x的值,因为gclosures了x:

 x = 0 def f(): def g(): return x * 2 return g closure = f() print(closure()) # 0 x = 2 print(closure()) # 4 

下面是闭包的一个典型用例 – GUI元素的callback(这可能是子类化button类的替代方法)。 例如,您可以构造一个函数,该函数将响应button按下而被调用,并“closures”父范围中处理点击所需的相关variables。 这样你就可以从相同的初始化函数连接相当复杂的接口,将所有的依赖关系构build到闭包中。

在Python中,闭包是一个函数的实例,它具有不变的variables绑定。

实际上, 数据模型在函数的__closure__属性描述中解释了这一点 :

无或包含函数自由variables绑定的单元格元组 。 只读

为了certificate这一点:

 def enclosure(foo): def closure(bar): print(foo, bar) return closure closure_instance = enclosure('foo') 

显然,我们知道我们现在有一个variables名为closure_instance的函数。 表面上,如果我们用一个对象bar调用它,它应该打印string'foo'以及任何string表示forms。

事实上,string“foo”绑定到函数的实例,我们可以通过访问cell_contents属性的元组中的第一个(也是唯一的)单元格的cell_contents属性来直接读取它。

 >>> closure_instance.__closure__[0].cell_contents 'foo' 

另外,在C API文档中描述了单元对象:

“单元”对象用于实现多个作用域引用的variables

我们可以certificate我们的闭包的用法,注意到'foo'被卡在函数中,并没有改变:

 >>> closure_instance('bar') foo bar >>> closure_instance('baz') foo baz >>> closure_instance('quux') foo quux 

没有什么可以改变它:

 >>> closure_instance.__closure__ = None Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: readonly attribute 

部分function

给出的例子使用闭包作为一个部分函数,​​但如果这是我们唯一的目标,相同的目标可以用functools.partial

 >>> from __future__ import print_function # use this if you're in Python 2. >>> partial_function = functools.partial(print, 'foo') >>> partial_function('bar') foo bar >>> partial_function('baz') foo baz >>> partial_function('quux') foo quux 

还有更复杂的closures,以及不适合部分function的例子,我会进一步certificate他们进一步的时间允许。

这里是一个Python3闭包的例子

 def closure(x): def counter(): nonlocal x x += 1 return x return counter; counter1 = closure(100); counter2 = closure(200); print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 2 " + str(counter2())) print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 1 " + str(counter1())) print("i from closure 2 " + str(counter2())) # result i from closure 1 101 i from closure 1 102 i from closure 2 201 i from closure 1 103 i from closure 1 104 i from closure 1 105 i from closure 2 202 

对我来说,“closures”是能够记住他们创build的环境的function。 这个function允许你在closures中使用variables或者方法,换句话说,你不能使用它们,因为它们不再存在,或者由于范围而无法使用。 让我们看看ruby中的这段代码:

 def makefunction (x) def multiply (a,b) puts a*b end return lambda {|n| multiply(n,x)} # => returning a closure end func = makefunction(2) # => we capture the closure func.call(6) # => Result equal "12" 

即使在“multiply”方法和“x”variables都不再存在的情况下,它也可以工作。 所有这些都是因为closures能力的缘故。

我所看到的封闭的最好的解释是解释这个机制。 它是这样的:

设想你的程序堆栈是一个简并树,每个节点只有一个子元素,单个叶子节点是你当前正在执行的过程的上下文。

现在放松一下每个节点只能有一个孩子的约束。

如果你这样做,你可以有一个构造('yield'),它可以从一个过程返回而不丢弃本地的上下文(例如,当你返回时它不会从堆栈中popup)。 下一次调用该过程时,调用将拾取旧的堆栈(树)框架,并继续执行停止的地方。