5
l = [1, 2, 3]
a,b,c = [lambda: n*n for n in l]
a()  #=> 9
b()  #=> 9
c()  #=> 9

Why is this? I expected a(), b(), c(), to be 1, 4, and 9.

Des
  • 9,195
  • 4
  • 18
  • 21
  • 1
    possible duplicate of [Weird closure behavior in python](http://stackoverflow.com/questions/11109838/weird-closure-behavior-in-python) – Ashwini Chaudhary Jan 25 '13 at 03:40
  • The value of `n` at the end of iteration is 3, and you're actually using that global variable `n` in your lambda's. So, each time you call a(),b()or c() they look for a global variable `n`. Try `del n` after the iteration and then call a(),b() or c(). – Ashwini Chaudhary Jan 25 '13 at 03:46
  • @AshwiniChaudhary -- Careful there. On python3.x, you'll get a `NameError` when you try to `del n` (provided that it isn't defined previously ...). But you're right about what will happen on python2.x ... – mgilson Jan 25 '13 at 03:56
  • @mgilson wow! that's interesting, I didn't knew that. But, can't seem to find that in http://docs.python.org/3.0/whatsnew/3.0.html. – Ashwini Chaudhary Jan 25 '13 at 04:09
  • 1
    @AshwiniChaudhary it's in there, [loop control variables are no longer leaked into the surrounding scope](http://docs.python.org/3.0/whatsnew/3.0.html#changed-syntax). – wim Jan 25 '13 at 04:12

1 Answers1

8

n isn't in a local closure of the functions.

try

a, b, c = [lambda n=n: n*n for n in l]

This "abuse" of the default parameter causes a local variable called n to be created for each function

Here is another way to create a closure in Python2

>>> L=[1, 2, 3]
>>> def fgen():
...     local_n = global_n
...     def f():
...         return local_n * local_n
...     return f
>>> a, b, c = [fgen() for global_n in L]
>>> a()
1
>>> b()
4
>>> c()
9

It won't work in Python3 though because the loop var in the list comprehension isn't leaked into the global scope

Python3 does a better job of preventing us using a global in the function, so you need to pass a parameter if you want to use a list comprehension

>>> L=[1, 2, 3]
>>> def fgen(param_n):
...     local_n = param_n
...     def f():
...         return local_n * local_n
...     return f
... 
>>> a, b, c = [fgen(n) for n in L]
>>> a()
1
>>> b()
4
>>> c()
9
>>> 
John La Rooy
  • 295,403
  • 53
  • 369
  • 502