# Python's lambda is broken!

I quite like Python for teaching. And people praise it for the lambda construct which is a bit like $\lambda$-abstraction in functional languages. However, it is broken!

To see how

lambda is broken, try generating a list of functions $[f_0, ..., f_9]$ where $f_i(n) = i + n$. First attempt:

 >>> fs = [(lambda n: i + n) for i in range(10)] >>> fs[3](4) 13 

Wait a minute, fs[3](4) ought to be 3 + 4 = 7! It looks like all 10 functions share the same “last” value of i, which is 9. Indeed:

 >>> [f(4) for f in fs] [13, 13, 13, 13, 13, 13, 13, 13, 13, 13] 

This is certainly unexpected. Let us try to get around the problem by not using lambda:

 >>> fs = [] >>> for i in range(10): ... def f(n): return i+n ... fs.append(f) ... >>> [f(4) for f in fs] [13, 13, 13, 13, 13, 13, 13, 13, 13, 13] 

Still not working, so the reason is deeper, probably in the way environments are handled. Maybe like this:

 >>> fs = [] >>> for i in range(10): ... def f(n, i=i): return i+n ... fs.append(f) ... >>> [f(4) for f in fs] [4, 5, 6, 7, 8, 9, 10, 11, 12, 13] 

Victory! But try explaining to students what is going on.

Just to be sure, Haskell does the right thing, of course!

 Prelude> let fs = [(\n -> i + n) | i <- [0..9]] Prelude> [f(4) | f <- fs] [4,5,6,7,8,9,10,11,12,13] 

What were the implementors of Python thinking?!

batterseapower

This is not just a Pythonic phenomenon. IIRC, both Ruby and C# (and doubtless more languages besides) all make this choice for closures over mutable variables. It definitely confuses you when you see it for the first time, but the alternative behaviour you want has the potential to be even more confusing in some situations IMHO.

Note also that:

• You can recover closures over immutable variables with local declarations quite easily (this is similar to what you show above):

for (int i = 0; i i' + x); }

• You cannot recover closures over mutable variables if you only close over immutable values without quite a bit of fiddling around with arrays or similar:

-- Now I want the 'incorrect' behaviour: for (int[] i = { 0 }; i[0] i[0] + x) }

It's swings and roundabouts as usual :-)

yeshuaaaron

I'm not particularly familiar with Python, but Lisp folks sometimes run into the same problem using lambda with loop. It's not a problem with lambda, but rather a question whether loop variables are assigned to on each iteration or whether a new binding is established. (See http://groups.google.com/group/comp.lang.lisp/msg/3838212e836ff0ca for a recent manifestation.) The difference is essentially:

Establish a binding i; For each integer in the range do: set i to the integer and evaluate the body of the iteration construct.

vs.

For each integer in the range: establish a new binding from i to the integer and evaluate the body the iteration construct.

I think the same issue is described here: http://mail.python.org/pipermail/python-list/2003-September/224902.html . It seems that Python's lambda isn't broken, but that there's not a convenient way to establish local bindings.

Indeed, consider:

>>> def makeAdder(i): return (lambda n: i + n) ...

>>> fs = [makeAdder(i) for i in range(10)]

>>> [f(4) for f in fs]

[4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

svat

Isn't this the same thing many languages do, and a "feature" of the implementation idea known as "closures"?

For example, in JavaScript:

var fs = {}; for(var i=0; i-5; i++) { fs[i]= function(n) { return n+i; } } for(var j in fs) { console.log("fs[" + j +"] = ",fsj); }

(used i-5 just in case Wordpress chokes on the less-than sign) gives fs0 = 110 fs1 = 110 fs2 = 110 fs3 = 110 fs4 = 110

Even worse,

var fs = {}; for(var i=0; i-5; i++) { fs[i]= function(n) { return n+i; } }

for(var i in fs) { console.log("fs" + i +" = ",fsi); }

gives

fs0 = 1000 fs1 = 1001 fs2 = 1002 fs3 = 1003 fs4 = 1004

but that can be blamed on JavaScript.

You could call it either a bug in the languages, or a failure on our part to understand the implications of late-binding and the dynamic goodness.

(Quite a pain to have to register before posting comments: have you considered OpenID?)

svat

Oops, those 110s should be 105s. (I had a loop running until 10 originally, then I changed it in the comment...)

mcmanus

Actually, the implementors of Python are no crazy (or maybe they are, but not for that reason). The fact is, Python is not primarily a functional language and thus variables are by default mutable object. When in your examples the closure is created, i is a reference in a way, not a value. Take the following Python example : >>> i = 1 >>> def f(): return i >>> f() 1 >>> i = 2 >>> f() 2 I think that everybody expect this behavior and nobody would want the second call to f() to return 1. So yes, I can get that in the example you show the comportement may seems surprising and yes I'm sure it is not the simpliest thing to explain to student but lambda in Python are not broken. They are just lambda in a non functional language.
Note that you can "emulate" the same comportement in Haskell by using references (but it will involve a monad and not be as clean as the 2 lines you give).

jrschulz

The problem is not the lambda expression but the fact that list/ comprehensions leak variables. AFAIK this is fixed in Python 3.0.

roberto

This works:

fs = [(lambda n, i=i: i + n) for i in range(10)]

The messages are dated 2002... :-(

jaked

Javascript is the same way (and Perl iirc). The brokenness is more to do with the for loop than with lambda: i points to the same location at each iteration, and the location is mutated. That makes perfect sense if you're coming from C, but interacts badly with lambda as you point out.

bcasiello

Looks like (as you say) the problem is not with lambda, but with the environment - the 'i' in the lambda, as with the named function, is bound to the global 'i':

>>> fs = [(lambda n: i + n) for i in range(10)] >>> fs3 13 >>> i=38 >>> fs3 42

The same 'fix' as in your def works for lambda as well: >>> fs = [(lambda n, i=i: i + n) for i in range(10)] >>> fs3 7

Lua, one of my favorite little languages, also gets it right: > f={} > for i = 0, 9 do f[i] = function(n) return i + n end end > print(f3) 7 > i=38 > print(f3) 7

combinatorics

At least there are many other ways to solve this problem. Your last example makes absolute sense as to how it should be done.

goodger

Before making such a sensational claim, you should do your homework. What's broken is your understanding of Python.

Python looks up names at run-time. Your lambdas functions reference the non-local name "i" (i.e., the name "i" is not defined in the local scope), so Python has to look outside the local scope. It finds that the current value of "i" is 9. In order to retain the value of "i" at lambda definition time, you need to declare local names:

>>> fs = [(lambda n, j=i: j + n) for i in range(10)] >>> fs3 7

Hint: default values are calculated at definition time, not run time. So "j=i" effectively curries the value of "i" at definition time. I used "j" inside the lambda to make it easier to understand; you should avoid using the same name in multiple scopes.

goodger

This is the very same lambda as is in Perl, Ruby, C#, (probably lisp and scheme, too). The brokenness here is more about list comprehensions/iterators using a single mutable variable instead of a new binding. In python it is particularly hard to get it right, because variables are def-scoped, rather than block scoped. "Obvious" optimizations for imperative programmers = hell for functional programmers.

Lambdas closing over mutable variables get hard to think about really fast.

justinbozonier

Not broken, though, admittedly, when I first ran into this I threw up in my mouth a little. C# actually handles lambdas the way you'd expect but I believe Actionscript 3 has the same issue as Python here. A much simpler looking change would be to do this:

fs = [(lambda n, i = i: i + n) for i in range(10)]

or for clarity

fs = [(lambda n, k = i: k + n) for i in range(10)]

which is also the same as doing this:

def statefulLambda(i): return lambda n: i + n

fs = [statefulLambda(i) for i in range(10)]

As luqui said, you basically need to capture the state of the i variable in a block. Since Python doesn't do that for you, it means you need to initialize a new variable to store that data in with each function you create. At least that's how I think about it.

jaux

If you're teaching imperative languages to students with FP background, you definitely want to give the example to them as a challenge ;)

disturbyte

Actually, a better way of doing it is to use lambdas the way Church made them:

fs = [(lambda i: lambda n: n + i)(j) for j in range(10)]
fs[3](4) == 7

ludwig

grainednoise

This has nothing to do with lambdas, but with the scoping rules of Python. Not being privy to the inner workings of the implementors' minds, I can only guess at 'what they were thinking', but it's probably something along the lines of 'simple to implement and easy to explain'.

Each call to a function/lambda creates a scope. Any function/lambda defined within that scope has access to that scope (albeit strictly read-only in Python 2.x) but all functions/lambdas defined within that call share the same outer scope.

Anyway, the fix is quite simple (and you basically found that solution yourself already):

fs = [(lambda n, addend=i: addend + n) for i in range(10)]

The trick here is that default values of parameters are evaluated just once when the function/lambda is created. This, too, is sometimes unknown to less experienced Python programmers. To quote a co-worker completely out of context: "It's really intuitive once it's explained to you!"

First a reply to ludwig: yes, thank you, that's what I get for changing the source without rechecking if it still works. I fixed it.

A reply to goodger: this is not a popularity contest. My motivation is not to get a maximum number of replies. I prefer fewer well though out replies than a lot of fly-by comments. If registration is too much burden for you, then don't register. Anyhow, thank you for your comment.

I thank everyone else for their explanations. I still wonder how I should explain this to my students (beginners, some of them have not seen any other programming language in their lives).

After seeing some of your posts, I discovered a much bigger horror in Python:

>>> i
Traceback (most recent call last):
File "", line 1, in
NameError: name 'i' is not defined
>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> i
9

Oh my, i is still defined after the list comprehension. This is certainly unexpected. Same thing for loops:

>>> j
Traceback (most recent call last):
File "", line 1, in
NameError: name 'j' is not defined
>>> for j in range(10): pass
...
>>> j
9

While I could accept the explanation about how closures capture the location and not the value, this really is a design error. The loop variable ough to be local to the loop. But I suppose I get what I deserve for using a dynamic language.

Reply to Andrej: no, it's not because of Python is dynamic. For example in Lua, a dynamic tiny language, but actually a pearl, this is what happens:

Name

E-mail

Website

Comment