Why is lambda expression necessary in this example? (Python)
Like you guessed, when you use command = showinfo(name)
you are asking python to immediately call showinfo(name)
, and then assign the result of that to the command
attribute.
The command
attribute must be given a reference to a function. lambda
is a convenient way to create an anonymous function and return the reference, which gets assigned to the attribute. Inside that anonymous function you can call any other function you want, and that inner code isn't executed until the anonymous function is executed.
The functional purpose of lambda
is to create a temporary, unnamed function that can be passed to other functions or stored as an attribute. It is a convenient way (but not the only way1) to create a wrapper around a callback that requires an argument.
1Another way to accomplish the same thing is with functools.partial. Another method would be to write your own decorator.
A callback is simply a function that you pass around to other functions so those functions can call it. showinfo(name)
is not a callback because the function is called immediately before the Button
is constructed and the return value of showinfo
seems to be None
(if a function doesn't return anything it returns None
by default in Python).
showinfo
by itself, could be a callback because it's a function, however the problem is that it requires a positional argument. In Python, positional arguments are required:
def f1(callback): callback()def f2(arg1): passf1(f2) # TypeError: f2() takes exactly 1 argument (0 given)
The way your code solves this is with a lambda expression that takes a default parameter that we define on-the-fly to be name. So what we are saying is, here is a function that by has a keyword argument arg
that by default is set to name
, when this function is called, call showinfo with that default argument:
btn = tkinter.Button(text = name, command=lambda arg=name: showinfo(arg))
But why do we need to use default arguments? Isn't that a complicated way of doing things? Yes, yes it is. You can simply do this instead:
btn = tkinter.Button(text = name, command=lambda: showinfo(name))
This is a lambda that takes no arguments and is a perfectly valid equivalent to what you're doing. This is because showinfo
and name
are part of the closure of the lambda you're creating, so they can be accessed by the lambda itself even after the lambda function has been passed to another function.
Yes, but it's necessary because each lambda function you pass in needs the value of name at the time that the lambda was created. Relying on a closure to keep the value of name
won't do the trick, because the value of name
changes on every iteration of the loop.