I have two questions concerning python assuming the code below:
Why it is not possible to pass a function with parentheses or parameters to add_command?
What shall I do if my CreateWindow function has to take parameters?
Here's the line of code:
filemenu.add_command(label="update...", command=CreateWindow)
Doing command=CreateWindow(some_argument) will cause CreateWindow to be executed immediately, and whatever it returns will be used as the parameter for command. Python isn't smart enough to guess that you want CreateWindow to be the callback and not its return value.
Use a lambda expression: filemenu.add_command(label="update...", command=lambda: CreateWindow(some_arguments, go_here))
This is effectively equivalent to:
def f():
CreateWindow(some_arguments, go_here)
filemenu.add_command(label="update...", command=f)
... But much shorter.
Related
This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 1 year ago.
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
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.
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).
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"))
Is it possible to have the below code without raising an exception?
The hello function represents code outside my control. It is only here for the sake of clarity.
def hello():
print("hellowed")
def callsCallback(callback):
callback(*["dd"])
callsCallback(hello)
The idea is for a library to receive a callback function for when something happens. For backwards compatibility, the function being called may or may not receive parameters.
I'm aware of this answer: How can I find the number of arguments of a Python function? but I'd rather avoid inspection, if I can.
If you used *args in a wrapper function then it would never throw an exception because of the incorrect number of arguments.
def hello():
print("hellowed")
def wrapper(f):
def g(*args):
f()
return g
def callsCallback(callback):
callback = wrapper(callback)
callback(*["dd"])
callsCallback(hello)
You could use a decorator style function. This may be overkill for what you want to do.
If you don't know ahead of time how many arguments hello takes, you would have to introspect as you suggested to call the function appropriately.
I explain:
I would like to know how I can pass method or functions as arguments.
For example, in Python will be:
from MyFile import MyClass
MyClass().my_method_click(function) # without parentheses
In this example, in Python you send the functions or method without
parentheses, if I do:
from MyFile import MyClass
MyClass().my_method_click(function()) # with parentheses
I call the function but don't send it.
In Ruby, when you call a method or function, you can do it with or
without parentheses.
if I do this in Ruby:
require_relative "MyClass"
MyClass.new.my_method_click(function) # without parentheses
Just call it without send it.
Of course, is for a Button, that when I click it, run this operation.
How I can do it in Ruby??
Thanks!
Basically, you want to pass a runnable block of code. I haven't looked into Python yet, but I am sure it supports closures as well.
Anyhow, in Ruby, a "general" way of passing a runnable code is to use blocks (lambdas and procs).
function = lambda { # your code }
MyClass.new.my_method_click(function)
# or a shorter way
MyClass.new.my_method_click(-> { # your code })
# to run a block
def my_method_click(&block)
#you can either `yield` from your receiving method
yield
# or call `.call` method on your lambda/proc instance
block.call
end
You can also get an instance of your class' method or create a new method one using Method.new. But, you'd end up dealing with bindings and binding to the correct instance types, etc. So, it's much easier with Lambdas and Procs.
If I have a function like this:
def any_function(type_name, field_name):
def another_function(name):
...
How would I go about testing any_function?
In case I used the wrong definition of testing, I mean by writing
print(any_function(...))
You wouldn't test an inner function. You'd just test the functionality of the outer function; the inner function is an implementation detail, not the API presented by the unit. You'd normally use an inner function to produce a closure, making the inner function dependent on the scope.
Now, if the inner function is returned by the outer, test that return value, like you would the product of any function.
If the inner function is independent and requires tests of its own, you shouldn't be nesting it.
Imagine another_function was deleted by mistake. Write a test to catch that.
Now imagine another_function is back again. How can you tell?
Imagine some code in another_function is deleted, or changed in some way by mistake. Write a test to detect that error.
A function creates a function to either a) use it or b) return it. In Python, b) is probably more common than a). Martijn Pieters covered a). For b), testing the outer function means calling it and then testing the function returned. One should usually do that for more than one set of inputs to the outer function, which is to say, for more than one returned function. An example:
import unittest as u
def adder(n):
return lambda m: m+n
class Test(u.TestCase):
def test_adder(self):
add3 = adder(3)
self.assertEqual((add3(-1), add3(0), add3(1)), (2, 3, 4))
add_3 = adder(-3)
self.assertEqual((add_3(-1), add_3(0), add_3(1)), (-4,-3,-2))
u.main()
For testing and debugging purposes only, put a globlal another_function statement at the start, but don't forget to remove it post-testing. This will make you able to debug that inner function after calling the outer function once to initialise it. Otherwise, since another_function() will be in the local scope of any_function(), put your debugging inside any_function().
def any_function(type_name, field_name):
global another_function # Delete line in functional versions
def another_function(name):
pass
any_function(None, None)
print(another_function(...))
Or
def any_function(type_name, field_name):
def another_function(name):
pass
print(another_function(...)) # Delete line in functional versions
any_function(None, None)
I want to have a function in main class which has parameters not only self.
class Ui_Form(object):
def clearTextEdit(self, x):
self.plainTextEdit.setPlainText(" ")
print("Script in Textbox is Cleaned!",)
x will be my additional parameter and I want clearTextEdit to be called by click.
self.pushButton_3.clicked.connect(self.clearTextEdit(x))
it does not allow me to write x as parameter in clicked. Can you help me!
Solution
This is a perfect place to use a lambda:
self.pushButton_3.clicked.connect(lambda: self.clearTextEdit(x))
Remember, connect expects a function of no arguments, so we have to wrap up the function call in another function.
Explanation
Your original statement
self.pushButton_3.clicked.connect(self.clearTextEdit(x)) # Incorrect
was actually calling self.clearTextEdit(x) when you made the call to connect, and then you got an error because clearTextEdit doesn't return a function of no arguments, which is what connect wanted.
Lambda?
Instead, by passing lambda: self.clearTextEdit(x), we give connect a function of no arguments, which when called, will call self.clearTextEdit(x). The code above is equivalent to
def callback():
return self.clearTextEdit(x)
self.pushButton_3.clicked.connect(callback)
But with a lambda, we don't have to name "callback", we just pass it in directly.
If you want to know more about lambda functions, you can check out this question for more detail.
On an unrelated note, I notice that you don't use x anywhere in clearTextEdit. Is it necessary for clearTextEdit to take an argument in the first place?