Search code examples
pythonlambda

lambda in for loop only takes last value


I have the code

options=["INFO", "WARNING", "DEBUG"]

for i in range(len(options)):
    option=options[i]
    __cMenu.add_command(
        label="{}".format(option), 
        command=lambda: self.filter_records(column, option)
   )

which creates and saves several lambdas that should capture different values of the local variable option. However, when these lambdas get used, they all behave as though options was set to "DEBUG", the last value it takes on in the loop.

I am guessing this has something to do with the garbage collection in that only the last option remains, but I cannot figure out how to avoid this.


Solution

  • Please read about minimal examples. Without reading your code, I believe you have run into a well known issue addressed in previous questions and answers that needs 2 lines to illustrate. Names in function bodies are evaluated when the function is executed.

    funcs = [lambda: i for i in range(3)]
    for f in funcs: print(f())
    

    prints '2' 3 times because the 3 functions are identical and the 'i' in each is not evaluated until the call, when i == 2. However,

    funcs = [lambda i=i:i for i in range(3)]
    for f in funcs: print(f())
    

    makes three different functions, each with a different captured value, so 0, 1, and 2 are printed. In your statement

    __cMenu.add_command(label="{}".format(option),
        command=lambda: self.filter_records(column, option))
    

    add option=option before : to capture the different values of option. You might want to rewrite as

    lambda opt=option: self.filter_records(column, opt)
    

    to differentiate the loop variable from the function parameter. If column changed within the loop, it would need the same treatment.