lambda en for loop solo toma el último valor

Conjunto de problemas:

El menú contextual debe mostrar las variables de filtro dinámicamente y ejecutar una función con parámetros definidos dentro de la callback. Las descripciones genéricas se muestran correctamente, pero la llamada a la función siempre se ejecuta con la última opción establecida.

Lo que he intentado:

#!/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() 

La salida actual que obtengo es:

Filtrar registro de conjunto de registros por 2 y DEBUG

No importa cuál de las tres opciones elija. Supongo que tiene algo que ver con la recolección de basura que solo queda la última opción, pero no puedo descubrir cómo evitar esto.

Se recomienda cualquier ayuda.

Por favor lea sobre ejemplos mínimos . Sin leer su código, creo que se ha topado con un problema bien conocido abordado en preguntas y respuestas anteriores que necesita 2 líneas para ilustrar. Los nombres en los cuerpos de función se evalúan cuando se ejecuta la función.

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

imprime ‘2’ 3 veces porque las 3 funciones son idénticas y la ‘i’ en cada una no se evalúa hasta la llamada, cuando i == 2. Sin embargo,

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

realiza tres funciones diferentes, cada una con un valor capturado diferente, por lo que se imprimen 0, 1 y 2. En su statement

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

add option=option antes : para capturar los diferentes valores de option . Es posible que desee volver a escribir como

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

para diferenciar la variable de bucle del parámetro de función. Si la column cambia dentro del bucle, necesitaría el mismo tratamiento.