List of lambda functions does weird things

Question:

This is so weird, if I make a list of lambda functions and with a for, I print the output of each function, everything works, but if I individually print the output of a single function like the first one, it gives me the output of the last function or I don’t even know what it gives me, for example:

X = [1,2,3,4]
L = []

for i in range(len(X)):
   L.append(lambda x: X[i]**x)

for i in range(len(X)):
   print(L[i](2))

This gives me:

1
4
9
16

That is correct, but if i want only the first one:

print(L[0](2))
# -> 16

And if I want the second one does the same, and so on, I checked the lambda functions were all different and they are. I don’t know what’s going on

Asked By: Tito Diego

||

Answers:

The lambda references the global variable i, so after the for loop, i==3, computing X[3]**2:

X = [1,2,3,4]
L = []

for i in range(len(X)):
   L.append(lambda x: X[i]**x)

for f in L:
    print(f(2))

Output:

16
16
16
16

A way to fix is to capture the current value of global i as a local parameter i when the function is defined:

X = [1,2,3,4]
L = []

for i in range(len(X)):
   L.append(lambda x, i=i: X[i]**x)  # capture i as a parameter

for f in L:
    print(f(2))

Output:

1
4
9
16
Answered By: Mark Tolonen

You are expecting the value of i to be part of the function, not the name. That’s not how function definitions work, either with def statements or lambda expressions.

I’d recommend defining a function maker, so that your expected function can close over the local value of i.

def make_function(i):
    return lambda x: X[i]*x*x

Now i refers not to a global variable i, but to a local variable i in the function call. Every call to make_function creates a different local variable initialized with the value of the argument, and that variable is what your function will refer to when it is called.

for i in range(len(X)):
   L.append(make_function(i))
Answered By: chepner

Your lambda closes around the variable i. Despite what the for loops make it look like, there’s actually only one variable i in your entire code, so when you call L[i](2) or L[0](2), you’re using the current value of i. Let’s look at your first example.

for i in range(len(X)):
    print(L[i](2))

Here, we call L[i] with the enclosing value of i, so this is really basically

for i in range(len(X)):
    print(X[i] ** 2)

On the other hand, in your second example

print(L[0](2))

The i value is the i from the final loop iteration above. To get around this, you need to explicitly capture the current value of i.

L.append(lambda x, i=i: X[i]**x)

This exploits one of Python’s least intuitive features to explicitly capture a value in a lambda.

Answered By: Silvio Mayolo
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.