Why lambda in loop requires default?

Ned Batchelder ned at nedbatchelder.com
Sun Mar 27 11:29:54 EDT 2016


On Sunday, March 27, 2016 at 9:55:16 AM UTC-4, g vim wrote:
> Given that Python, like Ruby, is an object-oriented language 

It turns out that "object-oriented" means very little, and lots
of languages that are object-oriented will behave differently
from each other, even where object behavior is concerned.

> why doesn't 
> this:
> 
> def m():
>    a = []
>    for i in range(3): a.append(lambda: i)
>    return a
> 
> b = m()
> for n in range(3): print(b[n]())  # =>  2  2  2
> 
> ... work the same as this in Ruby:
> 
> def m
>    a = []
>    (0..2).each {|i| a << ->(){i}}
>    a
> end
> 
> aa = m
> (0..2).each {|n| puts aa[n].()}  # =>  0  1  2
> 

Like Jussi, I don't know Ruby enough to tell you why Ruby *does*
work as you want, but I can help explain why Python doesn't work
as you want.

When you write "lambda: i", you are creating a closure, because
i is a name from outside the lambda.  In that case, the function
you create with the lambda refers to the variable i, not to the
value i had when you made the lambda.

Later, when you run the function, it will use the current value
of the variable i.  In this case, you have three functions, all
of which refer to the same i, and when you run them all, i is 2,
so they all produce 2.

> 
> lambda i=i: i
> 
> ... is needed to make it work in Python. Just wondered why?

Here you are using the local i as the default value for the
function parameter i.  Function parameter defaults are evaluated
when the function is defined, and the value is stored as part of
the function.  So all three of your functions store a different
value as the default for i.  When the functions are called, they
each use their distinct value as the default for the missing
argument, and they produce 0, 1, 2.

--Ned.



More information about the Python-list mailing list