Let's consider the following code :
def f(x):
return x
sol = [f]
sol.append(lambda x: sol[0](x) + x)
sol.append(lambda x: sol[1](x) + x)
print(sol[0](1), sol[1](1), sol[2](1))
# 1 2 3
Doing in such a way I got what I want. Now, let's do the exact same thing but with a for loop
:
def f(x):
return x
sol = [f]
for i in range(1, 3):
sol.append(lambda x: sol[i](x) + x)
print(sol[0](1), sol[1](1), sol[2](1))
# RecursionError: maximum recursion depth exceeded.
Can someone explain me what's happening ?
At first glance, I thought that the problem was similar to that one : lambda function calls itself repeatedly until it bottoms out at the recursion limit. But I do not see why that could be the case.
Your code creates a closure, so that when the lambdas are called, they do not use the value of i
that was in effect at the time they were created, but instead they use the current value of i
, which is 2. So it's equivalent to:
sol = [f]
sol.append(lambda x: sol[2](x) + x)
sol.append(lambda x: sol[2](x) + x)
To fix it, you need to capture the value of the loop index at the time the lambda is created. This can be done by lambda-binding the loop value (see the solutions below).
The other problem with the original code is that the loop index runs from 1 to 2, when in fact it needs to from from 0 to 1 (so that the lambdas refer to the previous functions in the list rather than to themselves).
Both problems can be fixed as follows:
sol = [f]
for i in range(0, 2):
sol.append((lambda i: lambda x: sol[i](x) + x)(i))
This produces the desired behavior. There's also a trick you can use to make it more concise:
sol = [f]
for i in range(0, 2):
sol.append(lambda x, i=i: sol[i](x) + x)
This binds i
as the default value of an optional second argument. The only difference in behavior is that the second argument can now be specified by the caller, which you may or may not want.