我们能解释闭包(因为它们与Python有关)吗?
我已经阅读了很多有关闭包的文章,我想我理解它们,但是我希望自己能为自己和其他人提供一个清晰而清晰的解释。我正在寻找一个简单的解释,可能有助于我理解在哪里以及为什么要使用它们。
解决方案
回答
很简单:一个函数可以从包含的作用域引用变量,可能是在控制流离开该作用域之后。最后一点非常有用:
>>> 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正确闭合。
回答
我从未听说过事务与解释闭包在同一上下文中使用,并且这里实际上没有任何事务语义。
之所以称为闭包,是因为它"封闭"了外部变量(常量),即,它不仅是一个函数,而且还是创建该函数的环境的包围。
在以下示例中,在更改x之后调用闭包g也会更改g中x的值,因为g在x上闭合:
x = 0 def f(): def g(): return x * 2 return g closure = f() print(closure()) # 0 x = 2 print(closure()) # 4
回答
我喜欢这个粗略,简洁的定义:
A function that can refer to environments that are no longer active.
我会加
A closure allows you to bind variables into a function without passing them as parameters.
接受参数的装饰器是闭包的常用用法。对于这种"功能工厂",关闭是一种常见的实现机制。当策略在运行时被数据修改时,我经常选择在策略模式中使用闭包。
在允许匿名块定义的语言(例如Ruby)中,C闭包可用于实现(相当于)新颖的新控制结构。缺少匿名块是Python中闭包的局限性。
回答
老实说,我完全了解闭包,除非我一直不清楚"闭包"到底是什么,以及"闭包"到底是什么。我建议我们放弃寻找术语选择背后的任何逻辑。
无论如何,这是我的解释:
def foo(): x = 3 def bar(): print x x = 5 return bar bar = foo() bar() # print 5
这里的一个关键思想是,即使'x'超出范围并且应该被取消,从foo返回的函数对象仍保留对本地var'x'的钩子。这个钩子指向的是var本身,而不仅仅是var当时的值,因此,在调用bar时,它将输出5,而不是3.
还应该清楚Python 2.x具有有限的闭包:我无法在'bar'内修改'x',因为写'x = bla'会在bar中声明一个本地'x',而不是赋给foo的'x' 。这是Python的Assignment = declaration的副作用。为了解决这个问题,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
回答
我见过的关于闭包的最好解释是解释该机制。它是这样的:
可以将程序堆栈想象成一棵简并树,其中每个节点只有一个孩子,而单个叶节点就是当前执行过程的上下文。
现在放宽每个节点只能有一个孩子的约束。
如果执行此操作,则可以有一个可从过程返回的构造(" yield"),而无需丢弃本地上下文(即,返回时不会将其弹出堆栈)。下次调用该过程时,该调用将拾取旧的堆栈(树)框架,并从中断处继续执行。
回答
封闭封闭
Objects are data with methods attached, closures are functions with data attached.
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
回答
这是GUI元素的闭包回调的典型用例(这是子类化Button类的替代方法)。例如,我们可以构造一个函数,该函数将响应按钮的按下而被调用,并"关闭"父作用域中处理单击所必需的相关变量。这样,我们可以通过相同的初始化函数连接非常复杂的接口,从而将所有依赖项构建到闭包中。