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

我读了很多关于闭包的文章,我认为我理解它们,但是在不影响我和其他人的情况下,我希望有人能够尽可能简洁明了地解释闭包。我正在寻找一个简单的解释,可能有助于我了解在哪里,为什么我想使用它们。

16046 次浏览

It's simple: A function that references variables from a containing scope, potentially after flow-of-control has left that scope. That last bit is very useful:

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

Note that 12 and 4 have "disappeared" inside f and g, respectively, this feature is what make f and g proper closures.

I've never heard of transactions being used in the same context as explaining what a closure is and there really aren't any transaction semantics here.

It's called a closure because it "closes over" the outside variable (constant)--i.e., it's not just a function but an enclosure of the environment where the function was created.

In the following example, calling the closure g after changing x will also change the value of x within g, since g closes over x:

x = 0


def f():
def g():
return x * 2
return g




closure = f()
print(closure()) # 0
x = 2
print(closure()) # 4

I like this rough, succinct definition:

A function that can refer to environments that are no longer active.

I'd add

A closure allows you to bind variables into a function without passing them as parameters.

Decorators which accept parameters are a common use for closures. Closures are a common implementation mechanism for that sort of "function factory". I frequently choose to use closures in the Strategy Pattern when the strategy is modified by data at run-time.

In a language that allows anonymous block definition -- e.g., Ruby, C# -- closures can be used to implement (what amount to) novel new control structures. The lack of anonymous blocks is among the limitations of closures in Python.

To be honest, I understand closures perfectly well except I've never been clear about what exactly is the thing which is the "closure" and what's so "closure" about it. I recommend you give up looking for any logic behind the choice of term.

Anyway, here's my explanation:

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


bar = foo()
bar()   # print 5

A key idea here is that the function object returned from foo retains a hook to the local var 'x' even though 'x' has gone out of scope and should be defunct. This hook is to the var itself, not just the value that var had at the time, so when bar is called, it prints 5, not 3.

Also be clear that Python 2.x has limited closure: there's no way I can modify 'x' inside 'bar' because writing 'x = bla' would declare a local 'x' in bar, not assign to 'x' of foo. This is a side-effect of Python's assignment=declaration. To get around this, Python 3.0 introduces the nonlocal keyword:

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

The best explanation I ever saw of a closure was to explain the mechanism. It went something like this:

Imagine your program stack as a degenerate tree where each node has only one child and the single leaf node is the context of your currently executing procedure.

Now relax the constraint that each node can have only one child.

If you do this, you can have a construct ('yield') that can return from a procedure without discarding the local context (i.e. it doesn't pop it off the stack when you return). The next time the procedure is invoked, the invocation picks up the old stack (tree) frame and continues executing where it left off.

Closure on closures

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

Here's a typical use case for closures - callbacks for GUI elements (this would be an alternative to subclassing the button class). For example, you can construct a function that will be called in response to a button press, and "close" over the relevant variables in the parent scope that are necessary for processing the click. This way you can wire up pretty complicated interfaces from the same initialization function, building all the dependencies into the closure.

For me, "closures" are functions which are capable to remember the environment they were created. This functionality, allows you to use variables or methods within the closure wich, in other way,you wouldn't be able to use either because they don't exist anymore or they are out of reach due to scope. Let's look at this code in 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"

it works even when both, "multiply" method and "x" variable,not longer exist. All because the closure capability to remember.

In Python, a closure is an instance of a function that has variables bound to it immutably.

In fact, the data model explains this in its description of functions' __closure__ attribute:

None or a tuple of cells that contain bindings for the function’s free variables. Read-only

To demonstrate this:

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


closure_instance = enclosure('foo')

Clearly, we know that we now have a function pointed at from the variable name closure_instance. Ostensibly, if we call it with an object, bar, it should print the string, 'foo' and whatever the string representation of bar is.

In fact, the string 'foo' is bound to the instance of the function, and we can directly read it here, by accessing the cell_contents attribute of the first (and only) cell in the tuple of the __closure__ attribute:

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

As an aside, cell objects are described in the C API documentation:

"Cell" objects are used to implement variables referenced by multiple scopes

And we can demonstrate our closure's usage, noting that 'foo' is stuck in the function and doesn't change:

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

And nothing can change it:

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

Partial Functions

The example given uses the closure as a partial function, but if this is our only goal, the same goal can be accomplished with 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

There are more complicated closures as well that would not fit the partial function example, and I'll demonstrate them further as time allows.

Here is an example of Python3 closures

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

Criteria to met by Closures are:

  1. We must have nested function.
  2. Nested function must refer to the value defined in the enclosing function.
  3. Enclosing function must return the nested function.

# 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

we all have used Decorators in python. They are nice examples to show what are closure functions in 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)

here final value is 12

Here, the wrapper function is able to access func object because wrapper is "lexical closure", it can access it's parent attributes. That is why, it is able to access func object.

I would like to share my example and an explanation about closures. I made a python example, and two figures to demonstrate stack states.

def maker(a, b, n):
margin_top = 2
padding = 4
def message(msg):
print('\n’ * margin_top, a * n,
' ‘ * padding, msg, ' ‘ * padding, b * n)
return message


f = maker('*', '#', 5)
g = maker('', '♥’, 3)
…
f('hello')
g(‘good bye!')

The output of this code would be as follows:

*****      hello      #####


      good bye!    ♥♥♥

Here are two figures to show stacks and the closure attached to the function object.

when the function is returned from maker

when the function is called later

When the function is called through a parameter or a nonlocal variable, the code needs local variable bindings such as margin_top, padding as well as a, b, n. In order to ensure the function code to work, the stack frame of the maker function which was gone away long ago should be accessible, which is backed up in the closure we can find along with the 'message's function object.