Python generating a lookup table of lambda expressions
Question: I'm building a game and in order to make it work, I need to generate a list of
"pre-built" or "ready to call" expressions. I'm trying to do this with lambda
expressions, but am running into an issue generating the lookup table. The
code I have is similar to the following:

    
    
    import inspect
    
    def test(*args):
        string = "Test Function: "
        for i in args:
            string += str(i) + " "
        print(string)
    
    funct_list = []
    
    # The problem is in this for loop
    for i in range(20):
        funct_list.append(lambda: test(i, "Hello World"))
    
    for i in funct_list:
        print(inspect.getsource(i))
    

The output I get is:

    
    
    funct_list.append(lambda: test(i, "Hello World"))
    funct_list.append(lambda: test(i, "Hello World"))
    funct_list.append(lambda: test(i, "Hello World"))
    funct_list.append(lambda: test(i, "Hello World"))
    ...
    

and I need it to go:

    
    
    funct_list.append(lambda: test(1, "Hello World"))
    funct_list.append(lambda: test(2, "Hello World"))
    funct_list.append(lambda: test(3, "Hello World"))
    funct_list.append(lambda: test(4, "Hello World"))
    ...
    

I tried both of the following and neither work

    
    
    for i in range(20):
        funct_list.append(lambda: test(i, "Hello World"))
    
    for i in range(20):
        x = (i, "Hello World")
        funct_list.append(lambda: test(*x))
    

My question is how do you generate lists of lambda expressions with some of
the variables inside the lambda expression already set.


Answer: As others have mentioned, Python's closures are _late binding_ , which means
that variables from an outside scope referenced in a closure (in other words,
the variables _closed over_) are looked up at the moment the closure is called
and _not_ at the time of definition.

In your example, the closure in question is formed when your lambda references
the variable `i` from the outside scope. However, when your lambda is called
later on, the loop has already finished and left the variable `i` with the
value 19.

An easy but not particularly elegant fix is to use a default argument for the
lambda:

    
    
    for i in range(20):
        funct_list.append(lambda x=i: test(x, "Hello World"))
    

Unlike closure variables, default arguments are bound early and therefore
achieve the desired effect of capturing the value of the variable `i` at the
time of lambda definition.

A better way is use `functools.partial` which allows you to partially apply
some arguments of the function, "fixing" them to a certain value:

    
    
    from functools import partial
    
    for i in range(20):
        funct_list.append(partial(lambda x: test(x, "Hello World"), i))
    

