How Is Tkinter Handling Lambda After Binding Event?
Solution 1:
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:
deff(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 = 0x = 0 y = 4x = 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:
deff(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 timesx=0 y = 0
x = 0 y = worldx=helloy= 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:
defsquare(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:
deff(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:
deff(i=None):
test2(i, inp_var2.get())
or:
deff(i='hello world'):
test2(i, inp_var2.get())
because when Tkinter calls f
, it always provides an actual argument for i
.
Post a Comment for "How Is Tkinter Handling Lambda After Binding Event?"