绑定事件之后,Tkinter如何处理lambda? [英] How is Tkinter handling lambda after binding event?
问题描述
我正在尝试编写一些代码,该代码将根据绑定将Entry
框的值发送给函数.从技术上讲,我可以在下面的代码中获得所需的行为,但是我a)不知道为什么会这样,并且b)非常确定我没有以最pythonic的方式执行此操作.我敢肯定我误解了event
或lambda
或两者.
在绑定触发时,在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
,因此x
和y
都绑定为零.对f
的 second 调用仅传递0
,因此x
绑定到0
,而y
绑定到其默认值4.第三 >调用完全不传递任何内容,并且x
和y
都绑定为其默认值.
(到目前为止,这一切都应该足够清楚.)
现在让我们大惊小怪.我将继续假定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
为什么这甚至在我们第一次调用它之前就读取x
和y
的默认值?为什么没有每次调用都读取x
和y
的新默认值?
想一想,然后继续阅读
真的,这样做.询问为什么输入发生在这些奇怪的时间.
现在您已经考虑过...
但是它做到了 .这就是关键.您的参数的默认值在执行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部分的工作方式相同.与f
和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
的行而不是现在调用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屋!