Prev: Windows - select.select, timeout and KeyboardInterrupt
Next: Picking a license (was Re: new extension generator for C++)
From: Artur Siekielski on 6 May 2010 15:34 Hello. I found this strange behaviour of lambdas, closures and list comprehensions: >>> funs = [lambda: x for x in range(5)] >>> [f() for f in funs] [4, 4, 4, 4, 4] Of course I was expecting the list [0, 1, 2, 3, 4] as the result. The 'x' was bound to the final value of 'range(5)' expression for ALL defined functions. Can you explain this? Is this only counterintuitive example or an error in CPython? Regards, Artur
From: Raymond Hettinger on 6 May 2010 16:25 On May 6, 9:34 pm, Artur Siekielski <artur.siekiel...(a)gmail.com> wrote: > Hello. > I found this strange behaviour of lambdas, closures and list > comprehensions: > > >>> funs = [lambda: x for x in range(5)] > >>> [f() for f in funs] > > [4, 4, 4, 4, 4] > > Of course I was expecting the list [0, 1, 2, 3, 4] as the result. The > 'x' was bound to the final value of 'range(5)' expression for ALL > defined functions. Can you explain this? Is this only counterintuitive > example or an error in CPython? Try binding the value of x for each of the inner functions: >>> funs = [lambda x=x: x for x in range(5)] >>> [f() for f in funs] [0, 1, 2, 3, 4] Otherwise, the 'x' is just a global value and the lambdas look it up at when the function is invoked. Really, not surprising at all: >>> x = 10 >>> def f(): .... return x .... >>> x = 20 >>> f() 20 Raymond
From: Emile van Sebille on 6 May 2010 16:26 On 5/6/2010 12:34 PM Artur Siekielski said... > Hello. > I found this strange behaviour of lambdas, closures and list > comprehensions: > >>>> funs = [lambda: x for x in range(5)] funs is now a list of lambda functions that return 'x' (whatever it currently is from whereever it's accessible when invoked) >>> [f() for f,x in zip(funs,range(5))] [0, 1, 2, 3, 4] >>> del x >>> [f() for f in funs] Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 1, in <lambda> NameError: global name 'x' is not defined >>> Emile
From: Benjamin Peterson on 6 May 2010 16:29 Artur Siekielski <artur.siekielski <at> gmail.com> writes: > > Of course I was expecting the list [0, 1, 2, 3, 4] as the result. The > 'x' was bound to the final value of 'range(5)' expression for ALL > defined functions. Can you explain this? Is this only counterintuitive > example or an error in CPython? The former. Closures are rebound in a loop.
From: Terry Reedy on 6 May 2010 21:49
On 5/6/2010 3:34 PM, Artur Siekielski wrote: > Hello. > I found this strange behaviour of lambdas, closures and list > comprehensions: > >>>> funs = [lambda: x for x in range(5)] >>>> [f() for f in funs] > [4, 4, 4, 4, 4] You succumbed to lambda hypnosis, a common malady ;-). The above will not work in 3.x, which does not leak comprehension iteration variables. It is equivalent to funs = [lambda: x for y in range(5)] del y # only for 2.x. y is already gone in 3.x x = 4 [f() for f in funs] Now, I am sure, you would expect what you got. and nearly equivalent to def f(): return x x=8 funs = [f for x in range(5)] [f() for f in funs] # [8,8,8,8,8] in 3.x Ditto Terry Jan Reedy |