Python closures oddity

So today, I came across some weird code.

def foo(val):
    print val

lambda_list = [ lambda: foo(i) for i in xrange(3) ]
for lambda__ in lambda_list:
    lambda__()

Don’t peek. Try to predict the output.

.
.
.
.
.
.
.
.

Surprising, isn’t it? I was certainly surprised to see the output come out as

2
2
2

Why’s this happening?

Well, it turns out that python closes on names, and not on values. What that means is, the value of i in our code is looked up only when the lambda__() function gets actually called within the for loop.

Solution?

1. Just pass in the value of i as a parameter to the function. Unfortunately, the problem is, we are calling the function as lambda__() without any parameters. But default arguments would work nicely in this case.

lambda_list = [ lambda i=i: foo(i) for i in xrange(3) ]

2. Use functools.partial to wrap the function and the argument in, and then call it later

lambda_list = [ functools.partial(foo, i) for i in xrange(3) ]

3. If you are a closeted Javascript programmer, you could also do this…

lambda_list = [ (lambda i: (lambda: foo(i)))(i) for i in xrange(3) ]  

But seriously, please don’t do this, its terribly unreadable.

Advertisements
Standard

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s