Callback function tkinter button with variable parameter [duplicate] - python

This question already has answers here:
tkinter creating buttons in for loop passing command arguments
(3 answers)
Closed 10 months ago.
from tkinter import *
F=Tk()
i=1
while i<10:
newButton = Button(F,text="Show Number",command=lambda:showNumber(i))
newButton.pack(side=TOP)
i+=1
def showNumber(nb):
print(nb)
F.mainloop()
All buttons return 10. Why ?
I want button 1 return 1, button 2 return 2...
Thank you very much for helping me

Your anonymous lambda functions are can be though of as closures (as #abernert points out, they're not actually closures in Python's case) - they "close over" the variable i, to reference it later. However, they don't look up the value at the time of definition, but rather at the time of calling, which is some time after the entire while loop is over (at which point, i is equal to 10).
To fix this, you need to re-bind the value of i to a something else for the lambda to use. You can do this in many ways - here's one:
...
i = 1
while i < 10:
# Give a parameter to the lambda, defaulting to i (function default
# arguments are bound at time of declaration)
newButton = Button(F, text="Show Number",
command=lambda num=i: showNumber(num))
...

This is explained in the Python FAQ: Why do lambdas defined in a loop with different values all return the same result?.
Quoting the FAQ answer:
This happens because x is not local to the lambdas, but is defined in the outer scope, and it is accessed when the lambda is called — not when it is defined…
In order to avoid this, you need to save the values in variables local to the lambdas, so that they don’t rely on the value of the global…
In other words, your new functions aren't storing the value of i, they're storing the variable i. And they're all storing the same variable i, which has the value 10 at the end of your loop. In fact, if you add an i = 'spam' right before F.mainloop(), you'll see that all the buttons now print out the string spam instead of a number.
This is very useful when you're trying to create closures—functions that can affect their defining environment.* But when you're not trying to do so, that can get in the way.
The simplest way around this is to use a parameter with a default value. Default values don't hold variables; just values, which are evaluated at the time the function is defined. So:
newButton = Button(F,text="Show Number", command=lambda num=i: showNumber(num))
* Note that in this case, there aren't actually any closures involved, because i is a global, rather than a local in the enclosing scope. But really, this is just because Python has special handling for globals and doesn't need a closure here; conceptually, if you think of there being one, you won't get into any trouble unless you start looking at the __closure__ or __code__ attributes.

Related

Why does button.clicked.connect gives unexpected output when i set callback for it in pyqt6? [duplicate]

Im trying to build a calculator with PyQt4 and connecting the 'clicked()' signals from the buttons doesn't work as expected.
Im creating my buttons for the numbers inside a for loop where i try to connect them afterwards.
def __init__(self):
for i in range(0,10):
self._numberButtons += [QPushButton(str(i), self)]
self.connect(self._numberButtons[i], SIGNAL('clicked()'), lambda : self._number(i))
def _number(self, x):
print(x)
When I click on the buttons all of them print out '9'.
Why is that so and how can i fix this?
This is just, how scoping, name lookup and closures are defined in Python.
Python only introduces new bindings in namespace through assignment and through parameter lists of functions. i is therefore not actually defined in the namespace of the lambda, but in the namespace of __init__(). The name lookup for i in the lambda consequently ends up in the namespace of __init__(), where i is eventually bound to 9. This is called "closure".
You can work around these admittedly not really intuitive (but well-defined) semantics by passing i as a keyword argument with default value. As said, names in parameter lists introduce new bindings in the local namespace, so i inside the lambda then becomes independent from i in .__init__():
self._numberButtons[i].clicked.connect(lambda checked, i=i: self._number(i))
UPDATE: clicked has a default checked argument that would override the value of i, so it must be added to the argument list before the keyword value.
A more readable, less magic alternative is functools.partial:
self._numberButtons[i].clicked.connect(partial(self._number, i))
I'm using new-style signal and slot syntax here simply for convenience, old style syntax works just the same.
You are creating closures. Closures really capture a variable, not the value of a variable. At the end of __init__, i is the last element of range(0, 10), i.e. 9. All the lambdas you created in this scope refer to this i and only when they are invoked, they get the value of i at the time they are at invoked (however, seperate invocations of __init__ create lambdas referring to seperate variables!).
There are two popular ways to avoid this:
Using a default parameter: lambda i=i: self._number(i). This work because default parameters bind a value at function definition time.
Defining a helper function helper = lambda i: (lambda: self._number(i)) and use helper(i) in the loop. This works because the "outer" i is evaluated at the time i is bound, and - as mentioned before - the next closure created in the next invokation of helper will refer to a different variable.
Use the Qt way, use QSignalMapper instead.

Pass the value of a push button chosen among many others [duplicate]

Im trying to build a calculator with PyQt4 and connecting the 'clicked()' signals from the buttons doesn't work as expected.
Im creating my buttons for the numbers inside a for loop where i try to connect them afterwards.
def __init__(self):
for i in range(0,10):
self._numberButtons += [QPushButton(str(i), self)]
self.connect(self._numberButtons[i], SIGNAL('clicked()'), lambda : self._number(i))
def _number(self, x):
print(x)
When I click on the buttons all of them print out '9'.
Why is that so and how can i fix this?
This is just, how scoping, name lookup and closures are defined in Python.
Python only introduces new bindings in namespace through assignment and through parameter lists of functions. i is therefore not actually defined in the namespace of the lambda, but in the namespace of __init__(). The name lookup for i in the lambda consequently ends up in the namespace of __init__(), where i is eventually bound to 9. This is called "closure".
You can work around these admittedly not really intuitive (but well-defined) semantics by passing i as a keyword argument with default value. As said, names in parameter lists introduce new bindings in the local namespace, so i inside the lambda then becomes independent from i in .__init__():
self._numberButtons[i].clicked.connect(lambda checked, i=i: self._number(i))
UPDATE: clicked has a default checked argument that would override the value of i, so it must be added to the argument list before the keyword value.
A more readable, less magic alternative is functools.partial:
self._numberButtons[i].clicked.connect(partial(self._number, i))
I'm using new-style signal and slot syntax here simply for convenience, old style syntax works just the same.
You are creating closures. Closures really capture a variable, not the value of a variable. At the end of __init__, i is the last element of range(0, 10), i.e. 9. All the lambdas you created in this scope refer to this i and only when they are invoked, they get the value of i at the time they are at invoked (however, seperate invocations of __init__ create lambdas referring to seperate variables!).
There are two popular ways to avoid this:
Using a default parameter: lambda i=i: self._number(i). This work because default parameters bind a value at function definition time.
Defining a helper function helper = lambda i: (lambda: self._number(i)) and use helper(i) in the loop. This works because the "outer" i is evaluated at the time i is bound, and - as mentioned before - the next closure created in the next invokation of helper will refer to a different variable.
Use the Qt way, use QSignalMapper instead.

assignment of variable after function definition in Python

I am new to Python so I am unsure about the difference in variable assignment before or after the function definition.
Specifically, the first example was adopted from Lutz's book.
def tester(start):
print("inside tester")
def nested(label):
print("inside nested")
print(label,nested.state)
nested.state += 1
print("done with nested")
nested.state = start
print("done with tester")
return nested
F = tester(0)
F('spam')
F.state
F.state
The objective of the code is to store the state information without using nonlocal.
I am unsure what nested.state means here. I am unsure because nested.state is used inside nested() function (i.e. nested.state +=1) and outside nested() function (i.e. nested.state = start).
I modified the code above to see whether Python accepts assigning variable after function declaration for nested() and to see whether there is any concept I am missing relating to function.variable call (i.e. nested.state call).
def tester(start):
def nested(label):
print(label, state)
state += 1 #replaced 'nested.state' with 'state' here
state = start #replaced 'nested.state' with 'state' here
return nested
F=tester(0)
F('spam')
F('ham')
Unfortunately, above code generates error local variable 'state' referenced before assignment. This tells me that I am missing some concept about function.variable (i.e. nested.state).
Can someone please help me understand three things:
I. why it is that the code with nested.state doesn't generate any error but state does?
II. what does nested.state mean? If nested.state is a mechanism to access function's variables, why is it that the call inside nested() function also uses nested.state and not state?
III. If nested.state is a mechanism to access variable inside function, then why is it that PyCharm fails to show state under dropdown when I type nested.?
I'd appreciate any help. I research SO, and couldn't find any explanation on such problems.
The reason the first code example worked is because it was assigning and referencing an attribute of the nested function object. The key concept to understand here, is that Python allows you to assign new, arbitrary attributes to objects - including functions:
>>> def func(a, b):
return a + b
>>> func(1, 2)
3
>>> func.attr = 5
>>> func.attr
5
The first code example takes advantage of this fact by using the nested function object to store the necessary state. This is the same concept as using any other object to store the state. It's particularly convenient, however, to use a function since it's readily available.
In the second example, a normal variable is used. Because of this, normal scoping rules apply which means simply that the state variable defined in tester is not the state variable being referenced in nested. Thus, an error is raised.
Actually, I think you're asking a question about scope in Python, ignoring your code, check this:
def scope_level_1():
variable = 'Scope_level_1'
def scope_level_2():
variable = 'Scope_level_2'
def core():
nonlocal variable
variable += '_TOUCHED_AND_MODIFIED_BY_CORE'
print(variable)
return core()
return scope_level_2()
scope_level_1()
# 'Scope_level_2_TOUCHED_AND_MODIFIED_BY_CORE'
Don't worry about the keyword nonlocal, treat it just as a declaring to make code more readable.
First, remember a += b is the same as a = a + b. So a must exist before getting to the +=.
Simply put, in the first example the function nested has an attribute called state (accessed by nested.state). It is an attribute, which means that once you tell nested that it has an attribute called state (you are doing this in line 9 when nested.state = start) it keep that attribute. So, in the first example nested.state exists when you get to the +=.
In the second example, you are declaring a variable called state in tester, and another variable called state in nested. The one in nested could be called potato for all that matters, because it is not the same variable. Therefore when you arrive to the +=, the variable state does not exist!

Python Lambdas and Variable Bindings [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 6 months ago.
I've been working on a basic testing framework for an automated build. The piece of code below represents a simple test of communication between two machines using different programs. Before I actually do any tests, I want to completely define them - so this test below is not actually run until after all the tests have been declared. This piece of code is simply a declaration of a test.
remoteTests = []
for client in clients:
t = Test(
name = 'Test ' + str(host) + ' => ' + str(client),
cmds = [
host.start(CMD1),
client.start(CMD2),
host.wait(5),
host.stop(CMD1),
client.stop(CMD2),
],
passIf = lambda : client.returncode(CMD2) == 0
)
remoteTests.append(t)
Anyhow, after the test is run, it runs the function defined by 'passIf'. Since I want to run this test for multiple clients, I'm iterating them and defining a test for each - no big deal. However, after running the test on the first client, the 'passIf' evaluates on the last one in the clients list, not the 'client' at the time of the lambda declaration.
My question, then: when does python bind variable references in lambdas? I figured if using a variable from outside the lambda was not legal, the interpretor would have no idea what I was talking about. Instead, it silently bound to the instance of the last 'client'.
Also, is there a way to force the resolution the way I intended it?
The client variable is defined in the outer scope, so by the time the lambda is run it will always be set to the last client in the list.
To get the intended result, you can give the lambda an argument with a default value:
passIf = lambda client=client: client.returncode(CMD2) == 0
Since the default value is evaluated at the time the lambda is defined, its value will remain correct.
Another way is to create the lambda inside a function:
def createLambda(client):
return lambda: client.returncode(CMD2) == 0
#...
passIf = createLambda(client)
Here the lambda refers to the client variable in the createLambda function, which has the correct value.
What happens is that your passIf argument, the lambda, refers to the variable client from the enclosing scope. It doesn't refer to the object the variable client refers to when it is created, but the variable itself. If you call these passIf after the loop has ended, that means they all refer to the last value in the loop. (In closure terminology, Python's closures are late-binding, not early-binding.)
Fortunately it's fairly easy to make a late-binding closure into an early-binding closure. You can do it by simply giving the lambda an argument with as default the value you want to bind:
passIf = lambda client=client: client.returncode(CMD2) == 0
That does mean the function gets that extra argument, and might mess things up if it gets called with an argument by accident -- or when you want the function to take arbitrary arguments. So another technique is to do it like this:
# Before your loop:
def make_passIf(client):
return lambda: client.returncode(CMD2) == 0
# In the loop
t = Test(
...
passIf = make_passIf(client)
)

What is "lambda binding" in Python? [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
I understand what are lambda functions in Python, but I can't find what is the meaning of "lambda binding" by searching the Python docs.
A link to read about it would be great.
A trivial explained example would be even better.
Thank you.
First, a general definition:
When a program or function statement
is executed, the current values of
formal parameters are saved (on the
stack) and within the scope of the
statement, they are bound to the
values of the actual arguments made in
the call. When the statement is
exited, the original values of those
formal arguments are restored. This
protocol is fully recursive. If within
the body of a statement, something is
done that causes the formal parameters
to be bound again, to new values, the
lambda-binding scheme guarantees that
this will all happen in an orderly
manner.
Now, there is an excellent python example in a discussion here:
"...there is only one binding for x: doing x = 7 just changes the value in the pre-existing binding. That's why
def foo(x):
a = lambda: x
x = 7
b = lambda: x
return a,b
returns two functions that both return 7; if there was a new binding after the x = 7, the functions would return different values [assuming you don't call foo(7), of course. Also assuming nested_scopes]...."
I've never heard that term, but one explanation could be the "default parameter" hack used to assign a value directly to a lambda's parameter. Using Swati's example:
def foo(x):
a = lambda x=x: x
x = 7
b = lambda: x
return a,b
aa, bb = foo(4)
aa() # Prints 4
bb() # Prints 7
Where have you seen the phrase used?
"Binding" in Python generally refers to the process by which a variable name ends up pointing to a specific object, whether by assignment or parameter passing or some other means, e.g.:
a = dict(foo="bar", zip="zap", zig="zag") # binds a to a newly-created dict object
b = a # binds b to that same dictionary
def crunch(param):
print param
crunch(a) # binds the parameter "param" in the function crunch to that same dict again
So I would guess that "lambda binding" refers to the process of binding a lambda function to a variable name, or maybe binding its named parameters to specific objects? There's a pretty good explanation of binding in the Language Reference, at http://docs.python.org/ref/naming.html

Categories