Python unexpected behaviour pt. 1: lambdas


Lambda's variables are evaluated during it's execution, not when it's declared

Consider this snippet:

functions = []
for x in range(1, 11):
    functions.append(lambda : print(x))

for f in functions:
    f()

When I first wrote some code like that, I thought the output would be like that:

1
2
3
.
.
10

Instead it printed '10' ten times:

10
10
.
.
10

The reason for that is simple: the variables we use in lambdas are evaluated only when the lambda is called. Python will call each lambda, and when it encounters the x variable, it will just take the object that the x is referencing. And because we call lambdas after we iterated over range(1, 11) the x variable will be 10 each time.

Dirty solution

In Python the keyword arguments are evaluated when the function is loaded, not when it is called. We can use this fact to get the result we want:

functions = []
for x in range(1, 11):
    functions.append(lambda tmp=x: print(tmp))

for f in functions:
    f()

The result:

1
2
3
.
.
10

This look a little bit weird, and I think I have never seen code like that - that's why I called it a dirty solution. A better solution would be to probably not use lambdas at all and use closures instead.

Better solution

functions = []


def some_clever_name(tmp):
    def wrapped():
        print(tmp)
    return wrapped


for x in range(1, 11):
    functions.append(some_clever_name(x))

for f in functions:
    f()

And now we are talking! This works because when some_clever_name function is called - Python creates a local variable tmp (that references the same object that x) which is later used when wrapped function is called.