Search code examples
pythonpython-3.xtkintertk-toolkittkinter-button

Understanding Python Lambda behavior with Tkinter Button


I would like to understand how a button is working using lambda. I have the following Python code:

from tkinter import *

def comando_click(mensagem):
    print(mensagem)

menu_inicial = Tk()
menu_inicial.geometry("500x250+200+200")

botao = Button(menu_inicial, text = "Executar", command=comando_click("Nova_Mensagem"))
botao.pack()

menu_inicial.mainloop()

But my button doesn't work when I click on it, it only shows the print once in the console when I run the code, I added some prints here in the question:

Problem Picture one

Well it seems that when I use the Lambda function in the button it works and I really would like to know why.

Lambda working button Picture one

I just added to the button the lambda :

botao = Button(menu_inicial, text = "Executar", command=lambda:comando_click("Nova_Mensagem"))

Lambda working button Picture two

Why with lambda it works? It shoudn't work without lambda too since lambda is basically a anonymous function?

I am extremely curious to understand why it works, thank you all for the help :)

Edit: I would like to thank you guys, now I finally understand what was going on and how Python was working. Thank you all very much :D


Solution

  • When you use () with a function name(func(args)), then it is immediately calling/invoking the function while python is executing the line, you do not want that. You want to ONLY call the function when the button is clicked. tkinter will internally call the function for you, all you have to do is give the function name.

    Why use lambda? Think of it as a function that returns another function, your code can be lengthened to:

    func  = lambda: comando_click("Nova_Mensagem")
    botao = Button(menu_inicial, text = "Executar", command=func)
    

    func is the function name and if you want to call it, you would say func(). And when you say command=comando_click("Nova_Mensagem") then command has the value returned by command click(because you call the function with ()), which is None and if I'm not wrong, if the given value is None, it will not be called by tkinter. Hence your function is executed just once because of () and as a result of calling the function, you are assigning the value of the function call(None) before the event loop starts processing the events.

    Some other methods:

    • Using partial from functools:
    from functools import partial
    
    botao = Button(.....,command=partial(comando_click,"Nova_Mensagem"))
    
    • Using a helper function:
    def helper(args):
        def comando_click():
            print(args)
    
        return comando_click
    
    botao = Button(...., command=helper("Nova_Mensagem"))
    

    IMO, lambdas are the easiest way to proceed with calling a function with arguments.