【问题标题】:Python tkinter NameError when using lambda command使用 lambda 命令时的 Python tkinter NameError
【发布时间】:2016-08-11 11:55:59
【问题描述】:

我是 Python 的 tkinter 模块的新手,因此我将不胜感激任何帮助,甚至解释为什么这根本不可能(如果是这样的话)。

我有一个(foo, bar, x, y) 形式的四元组列表;像这样:

    TUPLE_LIST = [('Hello', 'World', 0, 0),
                  ('Hovercraft', 'Eels', 50, 100),
                  etc.]

以及稍后的for 循环,它理想地将变量foo 实例化为带有文本bar 的按钮,每个按钮都具有将其各自的bar 添加到已定义的Entry 小部件的功能,然后将其放在坐标x, y:

    for foo, bar, x, y in TUPLE_LIST:
        exec("{0} = Button(self, text='{1}', command=lambda: self.update_text('{1}'))".format(foo, bar))
        eval(name).place(x=x, y=y)

按钮位置完美,但如果我点击其中一个,我会收到以下错误:

     Traceback (most recent call last):
      File "...\lib\tkinter\__init__.py", line 1550, in __call__
        return self.func(*args)
      File "<string>", line 1, in <lambda>
     NameError: name 'self' is not defined

我猜这与命令是用 lambda 定义的事实有关,因此本质上不是定义的“函数”,但我看到其他人定义了他们的按钮命令与 lambdas。那么它是否也与使用exec 有关?另外,这是update_text 的代码(如果重要的话):

    def update_text(self, new_char):
        old_str = self.text_box_str.get()
        # Above is a StringVar() defined in __init__ and used as textvariable in Entry widget creation.
        self.text_box_str.set(old_str + new_char)

任何帮助将不胜感激。提前致谢。

【问题讨论】:

    标签: python lambda tkinter nameerror


    【解决方案1】:

    这是一个非常糟糕的主意。根本没有理由干干动态创建变量名。它使您的代码更复杂、更难维护和更难阅读。

    如果您想按名称引用小部件,请使用字典:

    buttons = {}
    for foo, bar, x, y in TUPLE_LIST:
        buttons[foo] = Button(self, text=bar, ...)
        buttons[foo].place(...)
    

    至于self 的问题,没有足够的代码来说明问题所在。如果您使用的是类,则代码看起来没问题。如果你不是,你不应该使用self

    要创建一个更新小部件的函数,您只需将该小部件或该小部件的名称传递给该函数即可。目前尚不清楚您要更新哪个小部件。它似乎是一个条目小部件。我不知道您是否有一个所有按钮都必须更新的条目小部件,或者每个按钮一个条目。我会假设前者。

    在下面的示例中,我将展示如何将要更改的变量与要添加的文本一起传递给函数。此解决方案不使用 textvariable 属性,但您可以根据需要使用。只需传递它而不是小部件。

    buttons[foo] = Button(..., command=lambda widget=self.text_box, value=bar: self.update_text(widget, value))
    ...
    def update_text(self, widget, value):
        old_value = widget.get()
        new_value = old_value + value
        widget.delete(0, "end")
        widget.insert(0, new_value)
    

    当然,如果您所做的只是附加字符串,您可以将这四行替换为widget.insert("end", value)

    【讨论】:

    • 好的,很公平,但这仍然不能解决最初的问题。然后我将如何创建这些按钮,使每个按钮都有一个command,将自己的bar 添加到Entry 小部件?因为除了exec 调用中的字符串格式之外,我想不出任何方法来做到这一点,而这似乎就是导致问题的原因。是的,我正在使用类(但请注意,导致问题的 self 甚至不是我的)。
    • @ENPM:几乎从不存在您应该求助于在构造字符串上调用exec 的情况。我已经更新了我的答案以显示如何调用函数并将要更新的小部件传递给它。该函数不需要知道小部件的名称,可以给它一个小部件本身的引用。
    • 嗨,@BryanOakley,你的想法真的很棒。感谢您的帮助。
    猜你喜欢
    • 2015-08-26
    • 2013-04-19
    • 2020-07-23
    • 2022-01-06
    • 2023-02-24
    • 2021-05-08
    相关资源
    最近更新 更多