你能解释一下闭包(因为它们与Python有关)吗?

时间:2008-08-17 19:14:31

标签: python functional-programming closures

我一直在阅读很多关于闭包的内容,我认为我理解它们,但是没有为自己和他人蒙上阴影,我希望有人可以尽可能简洁明了地解释闭包。我正在寻找一个简单的解释,可以帮助我理解我想要使用它们的地点和原因。

13 个答案:

答案 0 :(得分:85)

Closure on closures

  

对象是带有方法的数据   附件,闭包是功能   数据附件。

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

答案 1 :(得分:43)

这很简单:一个函数引用包含范围的变量,可能在控制流离开该范围之后。最后一点非常有用:

>>> 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适当闭合。

答案 2 :(得分:14)

我喜欢this rough, succinct definition

  

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

我要添加

  

闭包允许您将变量绑定到函数而不将它们作为参数传递

接受参数的装饰器是闭包的常用用途。闭包是这种“功能工厂”的常见实现机制。在运行时通过数据修改策略时,我经常选择在Strategy Pattern中使用闭包。

在一种允许匿名块定义的语言中 - 例如,Ruby,C# - 闭包可用于实现(多少)新颖的控制结构。缺少匿名块是the limitations of closures in Python

答案 3 :(得分:14)

说实话,我完全理解封闭,除了我从来没有清楚究竟什么是“封闭”以及它是什么如此“封闭”。我建议你放弃寻找术语选择背后的任何逻辑。

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

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

bar = foo()
bar()   # print 5

这里的一个关键想法是,从foo返回的函数对象保留了对局部变量'x'的挂钩,即使'x'已经超出范围并且应该不存在。这个钩子是var本身,而不仅仅是var当时的值,所以当调用bar时,它会打印5而不是3。

另外要明确Python 2.x的封闭有限:我无法修改'bar'中的'x',因为写'x = bla'会在bar中声明一个本地'x',而不是指定给'x' 'foo。这是Python的赋值=声明的副作用。为了解决这个问题,Python 3.0引入了非本地关键字:

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

答案 4 :(得分:6)

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

它被称为闭包,因为它“关闭”外部变量(常量) - 即,它不仅仅是一个函数,而是创建函数的环境的外壳。

在下面的示例中,在更改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

答案 5 :(得分:3)

这是闭包的典型用例 - GUI元素的回调(这将是对按钮类进行子类化的替代方法)。例如,您可以构造一个函数,该函数将响应按下按钮而被调用,并“关闭”处理单击所需的父作用域中的相关变量。通过这种方式,您可以从相同的初始化函数中连接相当复杂的接口,将所有依赖项构建到闭包中。

答案 6 :(得分:1)

# A Closure is a function object that remembers values in enclosing scopes even if they are not present in memory.

# Defining a closure

# This is an outer function.
def outer_function(message):
    # This is an inner nested function.
    def inner_function():
        print(message)
    return inner_function

# Now lets call the outer function and return value bound to name 'temp'
temp = outer_function("Hello")
# On calling temp, 'message' will be still be remembered although we had finished executing outer_function()
temp()
# Technique by which some data('message') that remembers values in enclosing scopes 
# even if they are not present in memory is called closures

# Output: Hello

闭包符合的标准是:

  1. 我们必须有嵌套功能。
  2. 嵌套函数必须引用封闭函数中定义的值。
  3. 封闭函数必须返回嵌套函数。
  4. # Example 2
    def make_multiplier_of(n): # Outer function
        def multiplier(x): # Inner nested function
            return x * n
        return multiplier
    # Multiplier of 3
    times3 = make_multiplier_of(3)
    # Multiplier of 5
    times5 = make_multiplier_of(5)
    print(times5(3)) # 15
    print(times3(2)) #  6
    

答案 7 :(得分:1)

以下是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

答案 8 :(得分:1)

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

事实上,data model explains this在其功能描述中是"Cell" objects are used to implement variables referenced by multiple scopes__closure__属性:

  

无或元组元组包含函数自由变量的绑定。只读

为了证明这一点:

def enclosure(foo):
    def closure(bar):
        print(foo, bar)
    return closure

closure_instance = enclosure('foo')

显然,我们知道我们现在有一个从变量名closure_instance指向的函数。表面上,如果我们使用对象bar调用它,它应该打印字符串'foo'以及bar的字符串表示形式。

事实上,字符串' foo' 绑定到函数的实例,我们可以通过访问{{1}的元组中第一个(也是唯一的)单元格的cell_contents属性来直接读取它。 } attribute:

__closure__

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

  

{{3}}

我们可以证明我们的关闭用法,注意>>> closure_instance.__closure__[0].cell_contents 'foo' 卡在函数中并且没有改变:

'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 可以实现相同的目标

functools.partial

还有更复杂的闭包不适合部分功能示例,我会在时间允许的情况下进一步演示它们。

答案 9 :(得分:0)

我想分享我的例子和关于闭包的解释。我做了一个python示例,以及两个用于演示堆栈状态的数字。

*****      hello      #####

      good bye!    ♥♥♥

此代码的输出如下:

def astype(self, typecode):
        ""
        return self._rc(self.array.astype(typecode))

以下两个图显示堆栈和附加到功能对象的闭包。

when the function is returned from maker

when the function is called later

当通过参数或非局部变量调用函数时,代码需要局部变量绑定,例如margin_top,padding以及a,b,n。为了确保函数代码能够工作,很久以前就已经消失的制造商函数的堆栈框架应该是可访问的,这可以在我们可以找到的消息中备份,以及消息的功能。对象

答案 10 :(得分:0)

对我来说,“闭包”是能够记住它们所创造的环境的功能。此功能允许您在闭包内使用变量或方法,换句话说,您将无法使用它们,因为它们不再存在或由于范围而无法使用。让我们看看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"  

即使“倍增”方法和“x”变量都不再存在,它也能正常工作。所有都是因为关闭能力要记住。

答案 11 :(得分:0)

我们都在python中使用过装饰器。它们是展示python中闭包函数的好例子。

class Test():
    def decorator(func):
        def wrapper(*args):
            b = args[1] + 5
            return func(b)
        return wrapper

@decorator
def foo(val):
    print val + 2

obj = Test()
obj.foo(5)

这里的最终价值是12

这里,包装器函数能够访问func对象,因为包装器是“词法闭包”,它可以访问它的父属性。 这就是为什么它能够访问func对象。

答案 12 :(得分:-2)

我所见过的最好的解释是解释机制。它是这样的:

想象一下,您的程序堆栈是一个简并树,其中每个节点只有一个子节点,而单个叶节点是您当前正在执行的过程的上下文。

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

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