Home > Back-end >  Understanding Python Lambda behavior with Tkinter Button
Understanding Python Lambda behavior with Tkinter Button

Time:12-19

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 :)

CodePudding user response:

In this code:

command=comando_click("Nova_Mensagem")

you have called the comando_click function, once, and assigned the result (None) to the command argument. Nothing will happen when command is called (in fact you should get a TypeError exception because None is not callable).

In this code:

command=lambda:comando_click("Nova_Mensagem")

you have not actually called comando_click yet -- you have created a new function (using lambda) that will in turn call comando_click when it is called. Every time the button is clicked, your new function will get called.

If the lambda is confusing, you can do the exact same thing with a def like this:

def button_command():
    comando_click("Nova_Mensagem")

...

command=button_command  # no ()!  we don't want to actually call it yet!

The lambda expression is just an alternative to using def when you want to create a small single-use function that doesn't need a name (e.g. you want to make a function that calls another function with a specific argument, exactly as you're doing here).

CodePudding user response:

The issue is that with comando_click("Nova_Mensagem") you are executing the function. So command=None.

In the second case lambda:comando_click("Nova_Mensagem") is returning a lambda, that internally calls comando_click("Nova_Mensagem").

Fix: just put command=comando_click.

If you want to personalize the lambda with arguments you could write something like this:

def handler(args):
    def custom_handler():
        print(args)
    return custom_handler

botao = Button(menu_inicial, text = "Executar", command=handler("my custom string"))

CodePudding user response:

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.

  • Related