本文介绍了for 循环中的 lambda 只取最后一个值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题集:

上下文菜单应动态显示过滤器变量并使用回调中定义的参数执行函数.通用描述显示正确,但函数调用始终使用最后设置的选项执行.

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.

我尝试过的:

#!/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 和 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.

建议提供任何帮助.

推荐答案

请阅读最小示例.如果没有阅读您的代码,我相信您已经遇到了之前的问题和答案中解决的一个众所周知的问题,需要 2 行来说明.函数体中的名称在函数执行时被评估.

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())

打印 '2' 3 次,因为 3 个函数是相同的,并且每个函数中的 'i' 直到调用时才计算,当 i == 2 时.然而,

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())

生成三个不同的函数,每个函数都有不同的捕获值,因此打印了 0、1 和 2.在你的声明中

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))

:之前添加option=option来捕获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)

将循环变量与函数参数区分开来.如果 column 在循环内发生变化,则需要相同的处理.

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

这篇关于for 循环中的 lambda 只取最后一个值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-29 23:52