I'm trying to create functions inside of a loop:
functions = []
for i in range(3):
def f():
return i
functions.append(f)
Alternatively, with lambda:
functions = []
for i in range(3):
functions.append(lambda: i)
The problem is that all functions end up being the same. Instead of returning 0, 1, and 2, all three functions return 2:
print([f() for f in functions])
[0, 1, 2]
[2, 2, 2]
Why is this happening, and what should I do to get 3 different functions that output 0, 1, and 2 respectively?
You're running into a problem with late binding -- each function looks up i
as late as possible (thus, when called after the end of the loop, i
will be set to 2
).
Easily fixed by forcing early binding: change def f():
to def f(i=i):
like this:
def f(i=i):
return i
Default values (the right-hand i
in i=i
is a default value for argument name i
, which is the left-hand i
in i=i
) are looked up at def
time, not at call
time, so essentially they're a way to specifically looking for early binding.
If you're worried about f
getting an extra argument (and thus potentially being called erroneously), there's a more sophisticated way which involved using a closure as a "function factory":
def make_f(i):
def f():
return i
return f
and in your loop use f = make_f(i)
instead of the def
statement.