lambda in list comprehension acting funny

Steven D'Aprano steve+comp.lang.python at pearwood.info
Thu Jul 12 13:29:49 CEST 2012


On Thu, 12 Jul 2012 09:44:15 +0000, Alister wrote:

> On Wed, 11 Jul 2012 08:43:11 +0200, Daniel Fetchinson wrote:
> 
>>> funcs = [ lambda x: x**i for i in range( 5 ) ] 
[...]
> Having read Steve's explanation in the other thread (which I think has
> finally flipped the light switch on lambda for me) it only requires a
> minor change
> 
> funcs=[ lambda x,y=i:x**y for i in range(5) ]
> 
> although I cant actually think why this construct would be needed in
> practice, how are you actually using it

I would expect that the example given is just sample code demonstrating 
the problem. A slightly more realistic case might be something like 
defining a bunch of callbacks for, say, a GUI application.

For example, the good ol' calculator app, where you have ten buttons for 
digits 0-9. So you might give each button a callback function that 
inserts its own digit into the text field:

buttons = [make_button(name=str(i)) for i in range(10)]


That works fine. So now you go to add a callback to each one:

buttons = [make_button(name=str(i), callback=lambda: FIELD.insert(i))
           for i in range(10)]


and lo and behold, the ten buttons named "0" through "9" all insert 9. 
The reason is that the callback functions aren't given the value of i, 
but only the name[1] "i". By the time the callbacks are actually used, 
i == 9 and all the buttons share the same value.



[1] Technically closures may not store values by name, but the principle 
is sound: the function, when called later, looks up the current value of 
variable i, which is not necessarily the same as it was when the closure 
was originally defined.


-- 
Steven



More information about the Python-list mailing list