绑定事件之后,Tkinter如何处理lambda? [英] How is Tkinter handling lambda after binding event?

查看:101
本文介绍了绑定事件之后,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()

解决方案

这与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.第三 >调用完全不传递任何内容,并且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语句时被捕获 .将名称f绑定到我们的函数的def语句实际上是 run ,当Python在看到函数的主体后加载文件时,该语句会运行.在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函数,例如我们几乎立即绑定到名称square的匿名函数.

arguments部分的工作方式相同.与finput一样,如果我们绑定x:

square = lambda x=3: x * x

或:

square = lambda x=int(input('choose a default for x now> ')): x * x

lambda表达式本身执行时,

仅发生一次 . 功能现在具有具有默认值的变量x.

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

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

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)

,除了您不必在此处提供功能名称f之外.该函数的lambda变体没有名称,但仍然只是一个函数. (因此,当我们执行square = lambda ...时,lambda函数没有名称.名称square是变量的名称,而不是函数的名称.变量仅绑定到 函数,以便square(10)调用它.)

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

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

或:

def f(i='hello world'):
    test2(i, inp_var2.get())

因为Tkinter调用f时,它总是为i提供实际参数.

I am trying to write some code that will send the value of an Entry box to a function based on a binding. I can technically get the behavior I want in the code below, but I'm a) not sure why it works and b) am quite sure I am not doing this in the most pythonic way. I'm pretty sure I'm misunderstanding the event or lambda or both.

In the code below the Entry box called input_box1 when the binding triggers, the inp_var1.get() code only gets the default value, not anything that has been entered into the box. In other words, the test1 function will print...

Test1 Foo

... no matter what you type into the entry.

The binding on input_box2 works exactly as I expect. I type anything in there and click somewhere else and it prints the new entry. However I don't understand why my lambda doesn't want an event or why I need to repeat the inp_var2.get() call.

If anyone knows what's going under the hood I'd love to hear it! Here's the code:

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

解决方案

This has very little to do with Tkinter itself. It's also not so much connected to lambda as it is to Python in general.

Take both of those out of the equation, and consider the following Python program:

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

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

Assuming Python 3 (or from __future__ import print_function), when run, this prints:

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

That's because the first call to f passes 0, 0, so x and y are both bound to zero. The second call to f passes just 0 so x is bound to 0 and y is bound to its default value of 4. The third call passes nothing at all and x and y are both bound to their default values.

(So far, this should all be clear enough.)

Now let's fuss with this a bit. I'll continue to assume Python 3 so that input means what in Python 2 we have to use raw_input to achieve:

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

Before you run this sample program, think about what you expect it to do. Then run it—here's my result:

$ 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

Why did this read the default values for x and y before we even called it the first time? Why didn't it read new default values for x and y on each call?

Think about that for a bit, then read on

Really, do that. Ask why the inputs happened at these odd times.

Now that you've thought about it...

It did do that, though. That's the key here. The default values for your arguments are captured at the time the def statement is executed. The def statement, which binds the name f to our function, is actually run when Python loads the file, after seeing the body of the function. The function itself is run later, after Python has gotten to the print call and then to the first f call.

A lambda in Python is just a sort of anonymous function definition. Instead of:

def square(x):
    return x * x

we can write:

square = lambda x: x * x

The lambda expression defines a new function-like item, a lambda function—you can't use if/else type statements inside one of these, just expressions, and they automatically return the value of their expression. In this case our lambda function has one argument named x. The function returns x * x.

The outer assignment, square =, binds this lambda function to the name square, just as if we'd done def square(x) instead. So it's mostly just a syntactic trick: we can have a real function, like square, with arguments, or a limited lambda function that can only use expressions, like this anonymous function that we almost immediately bind to the name square.

The arguments part works the same either way though. Just as with f and input, if we bind x:

square = lambda x=3: x * x

or:

square = lambda x=int(input('choose a default for x now> ')): x * x

that happens just once, when the lambda expression itself executes. The function now has a variable x with a default value.

When, later, we call the function, we can provide a value for x, or let it default. If we don't provide a value, Python uses the default that it captured earlier, when we executed the line with lambda in it, rather than now, when we call the lambda function.

This all holds for Tkinter as well. You wrote:

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

but that's pretty much the same as writing:

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

input_box2.bind('<FocusOut>', f)

except that you don't have to come up with the function-name f here. The lambda variant of the function has no name, but it's still just a function. (For that matter, when we do square = lambda ..., the lambda function doesn't have a name. The name square is the name of a variable, not the name of the function. The variable is merely bound to the function, so that square(10) calls it.)

Anyway, later, when Tkinter has an event that matches <FocusOut>, Tkinter calls f ... or, if you used lambda, calls your unnamed lambda function. Tkinter provides one argument to that function; the one argument it provides is the event. So your default value for i in f above is irrelevant—you could do:

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

or:

def f(i='hello world'):
    test2(i, inp_var2.get())

because when Tkinter calls f, it always provides an actual argument for i.

这篇关于绑定事件之后,Tkinter如何处理lambda?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆