Python for循环中的lambda仅取最后一个值

Python for循环中的lambda仅取最后一个值,python,lambda,tkinter,contextmenu,Python,Lambda,Tkinter,Contextmenu,问题集: #!/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, fil

问题集:

#!/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()
上下文菜单应该动态显示过滤器变量,并使用回调中定义的参数执行函数。 通用描述正确显示,但函数调用始终使用最后一个设置选项执行

我尝试过的:

#!/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()
我得到的电流输出是:

按2筛选日志记录集并进行调试

无论我选择三个选项中的哪一个。我认为这与垃圾收集有关,只剩下最后一个选项,但我不知道如何避免

建议您提供任何帮助。

请阅读。在没有阅读代码的情况下,我相信您遇到了前面的问题和答案中提到的一个众所周知的问题,需要两行代码来说明。函数体中的名称在执行函数时进行求值

funcs = [lambda: i for i in range(3)]
for f in funcs: print(f())
打印“2”3次,因为这3个函数是相同的,并且直到调用时才计算每个函数中的“i”,此时i==2。但是,

funcs = [lambda i=i:i for i in range(3)]
for f in funcs: print(f())
生成三个不同的函数,每个函数具有不同的捕获值,因此打印0、1和2。在你的声明中

__cMenu.add_command(label="{}".format(option),
    command=lambda: self.filter_records(column, option))
之前添加
option=option
以捕获
option
的不同值。您可能想重写为

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

将循环变量与函数参数区分开来。如果
在循环中更改,则需要相同的处理。

Python中的闭包捕获变量,而不是值。例如,考虑:

def f():
    x = 1
    g = lambda : x
    x = 2
    return g()
调用
f()
的结果是什么?正确答案是2,因为lambda
f
捕获了变量
x
,而不是创建时的值1

例如,我们写:

L = [(lambda : i) for i in range(10)]
我们创建了一个包含10个不同lambda的列表,但它们都捕获了相同的变量
i
,因此调用
L[3]()
结果将是9,因为迭代结束时变量
i
的值是
9
(在Python中,理解不会为每次迭代创建新绑定;它只是不断更新相同的绑定)

在Python中经常能捕捉到的“Twitter”在捕获<强>值<强>时所需的语义是使用默认参数。在Python中,不同于C++,默认值表达式在函数定义时间(即创建lambda时)而不是在函数调用时被评估。

L = [(lambda j=i: j) for i in range(10)]
我们正在声明一个参数
j
,并将创建lambda时
i
的当前值设置为默认值。这意味着当调用例如
L[3]()
时,由于“隐藏”参数的默认值,这次的结果将是3(调用
L[3](42)
当然将返回42)

更多的时候,你会看到更令人困惑的形式

lambda i=i: ...

其中“hidden”参数的名称与我们要获取其值的变量的名称相同。

我知道我迟到了,但我发现了一个混乱的解决方法,可以完成任务(在Python 3.7中测试)

如果使用双lambda(如我所说,非常混乱),则可以保留该值,如下所示:

步骤1:创建嵌套lambda语句:

send_param=lambda val:lambda:print(val)
步骤2:使用lambda语句:

send_参数(i)
send_-param
方法返回最内部的lambda(
lambda:print(val)
),而不执行语句,直到调用不带参数的
send_-param
的结果,例如:

a=发送参数(i)
()
只有第二行将执行
print
语句