绑定事件之后,Tkinter如何处理lambda?

时间:2019-03-10 23:35:23

标签: python tkinter lambda

我正在尝试编写一些代码,该代码将根据绑定将Entry框的值发送给函数。从技术上讲,我可以在下面的代码中获得所需的行为,但是我a)不知道为什么会这样,并且b)非常确定我没有以最pythonic的方式执行此操作。我敢肯定我误解了eventlambda或两者。

在绑定触发时,在Entry框下方的代码input_box1中,inp_var1.get()代码仅获得默认值,而没有输入任何默认值。换句话说,test1函数将打印...

  

Test1 Foo

...无论您输入什么内容。

input_box2上的绑定完全符合我的预期。我在其中键入任何内容,然后单击其他位置,它会打印新条目。但是我不明白为什么我的lambda不需要event或为什么我需要重复inp_var2.get()通话。

如果有人知道引擎盖下发生了什么,我很想听听!这是代码:

from tkinter import *
from tkinter import ttk

def test1(event, i):
    print('Test1', i)

def test2(event, i):
    print('Test2', i)

root = Tk()
title_label = Label(root, text='This does not work!')
title_label.grid(column=0, row=0)

inp_var1 = StringVar(value='Foo')
input_box1 = Entry(root, textvariable=inp_var1)
input_box1.grid(column=0, row=1)

inp_var2 = StringVar(value='Bar')
input_box2 = Entry(root, textvariable=inp_var2)
input_box2.grid(column=0, row=2)

input_box1.bind('<FocusOut>', lambda event, i=inp_var1.get(): test1(event, i))
input_box2.bind('<FocusOut>', lambda i=inp_var2.get(): test2(i, inp_var2.get()))

root.mainloop()

1 个答案:

答案 0 :(得分:6)

这与Tkinter本身无关。与lambda的连接也没有那么多,通常与Python的连接也没有。

将这两个都排除在外,并考虑以下Python程序:

def f(x=3, y=4):
    print('x =', x, 'y =', y)

f(0, 0)
f(0)
f()

假设Python 3(或from __future__ import print_function)在运行时显示:

x = 0 y = 0
x = 0 y = 4
x = 3 y = 4

这是因为对f first 调用传递了0, 0,因此xy都绑定为零。对f second 调用仅传递0,因此x绑定到0,而y绑定到其默认值4. third 调用完全不传递任何内容,并且xy均已绑定为其默认值。

(到目前为止,这一切都应该足够清楚。)

现在让我们大惊小怪。我将继续假设使用Python 3,以便input表示在Python 2中我们必须使用raw_input来实现:

def f(x=input('enter default x: '), y=input('enter default y: ')):
    print('x =', x, 'y =', y)

print('about to call f three times')
f(0, 0)
f(0)
f()

在运行此示例程序之前,请考虑您希望它执行的操作。然后运行它-这是我的结果:

$ python3 t.py
enter default x: hello
enter default y: world
about to call f three times
x = 0 y = 0
x = 0 y = world
x = hello y = world

为什么在我们甚至第一次调用它之前,都读取了xy 的默认值?为什么没有在每次通话时都读取xy的新默认值?

想一想,然后继续阅读

真的,这样做。问问为什么在这些奇怪的时间发生输入。

现在您已经考虑过...

但是

它做到了 。这就是关键。您的参数的默认值在执行def语句时被捕获 。当def语句将名称f绑定到我们的函数时,实际上是在看到函数体后Python加载文件时 run 。在Python进行print调用,然后进行第一个f调用之后,函数本身将稍后运行。

Python中的lambda只是一种匿名函数定义。代替:

def square(x):
    return x * x

我们可以写:

square = lambda x: x * x

lambda表达式定义了一个类似于函数的新项,即 lambda函数-您不能在其中一个中使用if / else类型的语句这些,只是表达式,它们会自动返回其表达式的值。在这种情况下,我们的lambda函数具有一个名为x的参数。该函数返回x * x

外部赋值square =将此lambda函数绑定到名称square,就像我们做了def square(x)一样。因此,这基本上只是一个语法技巧:我们可以有一个带有参数的实函数,例如square,或者一个只能使用表达式的有限的lambda函数,例如这个几乎立即绑定到名称{{ 1}}。

参数部分的工作方式相同。与squaref一样,如果我们绑定input

x

或:

square = lambda x=3: x * x

仅在square = lambda x=int(input('choose a default for x now> ')): x * x 表达式本身执行时一次发生。 功能现在具有一个变量lambda,具有默认值。

稍后,当我们调用该函数时,我们可以为x提供一个值,或者将其设置为默认值。如果我们不提供值,则当我们执行其中带有x的行时(而不是现在)调用lambda函数时,Python将使用它捕获更早的默认值。

这对Tkinter也同样适用。您写道:

lambda

但这与写作几乎相同:

input_box2.bind('<FocusOut>', lambda i=inp_var2.get(): test2(i, inp_var2.get()))

,除了您不必在此处提供函数名def f(i=inp_var2.get()): test2(i, inp_var2.get()) input_box2.bind('<FocusOut>', f) 。该函数的lambda变体没有名称,但仍然只是一个函数。 (因此,当我们执行f时,lambda函数没有名称。名称square = lambda ...是变量的名称,而不是函数的名称。变量只是< em>绑定到该函数,以便square对其进行调用。)

无论如何,稍后,当Tkinter的事件与square(10)相匹配时,Tkinter会调用<FocusOut> ...,或者,如果您使用了f,则会调用您的未命名的lambda函数。 Tkinter为该函数提供了一个参数。它提供的一个参数是事件。因此,上面lambdai的默认值是无关紧要的,您可以这样做:

f

或:

def f(i=None):
    test2(i, inp_var2.get())

因为Tkinter调用def f(i='hello world'): test2(i, inp_var2.get()) 时,它总是为f提供实际参数。