lambda in for loop only takes last value
Question: **Problemset:**

Context Menu should show filter variables dynamically and execute a function
with parameters defined inside the callback. Generic descriptions show
properly, but function call is always executed with last set option.

**What I have tried:**

    
    
    #!/usr/bin/env python
    
    import Tkinter as tk
    import ttk
    from TkTreectrl import MultiListbox
    
    class SomeClass(ttk.Frame):
        def __init__(self, *args, **kwargs):
            ttk.Frame.__init__(self, *args, **kwargs)
            self.pack(expand=True, fill=tk.BOTH)
    
            self.grid_rowconfigure(0, weight=1)
            self.grid_columnconfigure(0, weight=1)
    
            self.View=MultiListbox(self)
    
            __columns=("Date","Time","Type","File","Line","-","Function","Message")
            self.View.configure(columns=__columns, expandcolumns=(0,0,0,0,0,0,0,1))
    
            self.View.bind("", self.cell_context)
            self.View.grid(row=0, column=0, sticky=tk.NW+tk.SE)
    
            self.__recordset          = []
            self.__recordset_filtered = False
    
            #Some dummy values
            self.__recordset.append(["Date", "Time", "INFO", "File", "12", "-", "Function", "Message Info"])
            self.__recordset.append(["Date", "Time", "DEBUG", "File", "12", "-", "Function", "Message Info"])
            self.__recordset.append(["Date", "Time", "WARNING", "File", "12", "-", "Function", "Message Info"])
    
            self.__refresh()
    
        def cleanView(self):
            self.View.delete(0, tk.END)
    
        def __refresh(self):
            self.cleanView()
            for row in self.__recordset:
                self.View.insert(tk.END, *row)
    
        def filter_records(self, column, value):
            print("Filter Log Recordset by {column} and {value}".format(**locals()))
            # Filter functionality works as expected
            # [...]
    
        def cell_context(self, event):
            __cMenu=tk.Menu(self, tearoff=0)
    
            if self.__recordset_filtered:
                __cMenu.add_command(label="Show all", command=lambda: filter_records(0, ""))
    
            else:
                column=2
                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))
                # Also tried using for option in options here with same result as now
            __cMenu.post(event.x_root, event.y_root)
    
    if __name__=="__main__":
        root=tk.Tk()
        app=SomeClass(root)
        root.mainloop()
    

The current output i get is:

> Filter Log Recordset by 2 and DEBUG

No matter which of the three options i choose. I assume it has sth to do with
the garbage collection that only the last option remains but i cannot figure
out how to avoid this.

Any help is recommended.


Answer: Please read about [minimal examples](https://stackoverflow.com/help/mcve).
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.

