About closing python

Below is an example from my python closure blog. I run it in python 2.7 and get output different from my expectation.

flist = [] for i in xrange(3): def func(x): return x*i flist.append(func) for f in flist: print f(2) 

My expected result: 0, 2, 4
But output: 4, 4, 4
Can anyone help explain this?
Thank you in advance.

+7
source share
3 answers

Loops do not introduce a scope in Python, so all three functions are closed by the same variable i and will refer to its final value after the loop ends, which is 2.

It seems that almost everyone with whom I speak, who uses closure in Python, have been bitten by this. The consequence is that an external function can change i , but an internal function cannot (since that would make i local rather than a closure based on Python syntax rules).

There are two ways to solve this problem:

 # avoid closures and use default args which copy on function definition for i in xrange(3): def func(x, i=i): return x*i flist.append(func) # or introduce an extra scope to close the value you want to keep around: for i in xrange(3): def makefunc(i): def func(x): return x*i return func flist.append(makefunc(i)) # the second can be simplified to use a single makefunc(): def makefunc(i): def func(x): return x*i return func for i in xrange(3): flist.append(makefunc(i)) # if your inner function is simple enough, lambda works as well for either option: for i in xrange(3): flist.append(lambda x, i=i: x*i) def makefunc(i): return lambda x: x*i for i in xrange(3): flist.append(makefunc(i)) 
+16
source

You do not create a closure. You create a list of functions, each of which receives the global variable i , which is 2 after the first cycle. This way you get 2 * 2 for every function call.

+4
source

Each function refers to global i .

functools.partial comes to the rescue:

 from functools import partial flist = [] for i in xrange(3): def func(x, multiplier=None): return x * multiplier flist.append(partial(func, multiplier=i)) 
+1
source

All Articles