0

in the process of learning Python. I understand the use of lambda, here's an example and link(for others).

Why are Python lambdas useful?

mult3 = filter(lambda x: x % 3 == 0, [1, 2, 3, 4, 5, 6, 7, 8, 9])

Would assign [3, 6, 9] to mult3.


Here is the code I was required to do in the book's quiz:

import tkinter

def counter(text):
    """Add 1 every time the button is pressed"""

    count = int(text.get())
    text.set(count + 1)


window = tkinter.Tk()
frame = tkinter.Frame(window)
frame.pack()

var = tkinter.IntVar()
var.set(0)

button = tkinter.Button(window, textvariable=var, command=lambda: counter(var))
button.pack()

tkinter.mainloop()

That is the verified solution. What I had come up with was the same except for the code in variable button. Here's my line:

button = tkinter.Button(window, textvariable=var, command=counter(var))

The absence of lambda prevents the counter from working. I don't understand why lambda is required? As I'm just calling a function I had made, I'm not requiring a new unnamed function..?

Thanks in advance for your time :)

Yabusa
  • 579
  • 1
  • 6
  • 15
  • In your version of the code `counter(var)` is only executed once. In their version it is executed every time the button is clicked. –  May 01 '18 at 00:05
  • Do you know why though? I'm trying to understand – Yabusa May 01 '18 at 00:16
  • Think about it more generally. When you pass a function arguments like `f(3 + 7)`, you're not passing `3 + 7` into the function, you're passing `10`. – Patrick Haugh May 01 '18 at 00:21
  • @Yabusa: what do you mean "why?" There is no deeper reason than that it is how the language works. In one case the argument to Button is a function, in the other case it is the result of a function call. –  May 01 '18 at 17:11

1 Answers1

2

There are a couple of things going on here. First, When python compiles command=counter(var), it calls the function and assigns its result to command. You called counter and your callback is now None and that's obviously not what you want.

You can't just do command=counter which would simply make counter the function to use in the callback because the API doesn't have a way to include arguments for the callback. So you do

command=lambda:counter(var)

This assigns an anonymous function that takes no parameters to command just like the API wants. The lambda contains a reference to var in the containing scope which it then uses to call counter. And that's how you created a parameter for the target callback you really wanted.

This reference is a bit strange. Its a reference to the var slot in the containing scope, not the object that happens to be in var at the time of assignment. If you change var before the callback is made (or the containing scope disappears as in locals disappearing on return from a function), that new object is used. So, put your code in a function so you don't drive yourself crazy!

tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • 1
    You can also use [functools.partial](https://docs.python.org/3/library/functools.html#functools.partial) to bind `var` in a closure. `tkinter.Button(window, textvariable=var, command=partial(counter, var))` – Håken Lid May 01 '18 at 00:39