Search code examples
pythonlambdaclosures

Creating functions (or lambdas) in a loop (or comprehension)


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])
  • Expected output: [0, 1, 2]
  • Actual output: [2, 2, 2]

Why is this happening, and what should I do to get 3 different functions that output 0, 1, and 2 respectively?


Solution

  • 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.