Can you explain closures (as they relate to Python)? Can you explain closures (as they relate to Python)? python python

Can you explain closures (as they relate to Python)?


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 counterc1 = make_counter()c2 = make_counter()print (c1(), c1(), c2(), c2())# -> 1 2 1 2


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.


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 barbar = 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 foobar()   # print 7