lambda in list comprehension acting funny

Steven D'Aprano steve+comp.lang.python at pearwood.info
Wed Jul 11 19:47:34 EDT 2012


On Wed, 11 Jul 2012 11:38:18 -0700, woooee wrote:

> You should not be using lambda in this case 
> .for x in [2, 3]:
> .    funcs = [x**ctr for ctr in range( 5 )] 
> .    for p in range(5):
> .        print x, funcs[p]
> .    print

If you change the requirements, it's always easy to solve problems. But 
it is the wrong problem that you have solved.

The problem we have been asked to solve is to create a sequence of 
function objects, so that they can be called later, when needed, *not* to 
pre-calculate the results.

In this case, the most obvious solution is to store a local variable in 
each function object with the value you want.

funcs = [(lambda x, i=i: x**i) for i in range(5)]

creates a list of five functions:

    lambda x, i=0: x**i
    lambda x, i=1: x**i
    lambda x, i=2: x**i
    and so on.

In this case, each function has two local variables, x and i, with i 
having a default value. The function parameter i is bound to the value of 
i when the function was created.

Because parameter defaults are calculated once, when the function is 
created, this causes the value of i to stick to the newly created 
function, and we get the result we need.

What happens if you don't use a parameter with a default value?

funcs = [(lambda x: x**i) for i in range(5)]

In this case, each function body contains one local variable, x, and one 
non-local or global variable, i.

Because i is a non-local, the function doesn't store a value for it. 
Instead, the function stores a lump of data pointing to just enough of 
the environment to fetch the current value of the non-local i when 
needed. Since all five functions are in the same environment, they all 
see the same value of i when you call them, regardless of what the value 
of i was when they were created.

This is little different from doing this:

i = 1
def f1(x): return x**i

i = 2
def f2(x): return x**i

i = 3
def f3(x): return x**i

Is there any surprise that all three functions return the same value? 
They all point to the same global variable i. I'm not sure what it is 
about lambda that fools people into thinking that it is different (I've 
even been fooled myself!) but it is not.


-- 
Steven



More information about the Python-list mailing list