Tkinter OptionMenu add a command to multiple OptionMenus - python

Basically I have a series of OptionMenus that are created in a loop, but is currently empty:
option_menu = []
for ii in range(jj):
option_menu.append([])
for ll in range(kk):
option_menu[ii].append(OptionMenu(frame,tkinter_text_var[ii][ll],''))
Then elsewhere I use a checkbox to set the values along the lines of:
for ii in range(jj):
for ll in range(kk):
option_menu[ii][ll]["menu"].add_command(label = name_from_box.get(), command = lambda: tkinter_text_var[ii][ll].set(name_from_box.get()))
This works to populate all of the OptionMenus properly, but when I select a value in any of the OptionMenus, it only sets option_menu[jj][kk] (i.e. that last one made).
So what have I done wrong?

This is a very common question involving closures. Look at the following example:
alist = [lambda : x for x in range(10) ]
print (alist[2]()) #9
print (alist[4]()) #9
The'll all be 9. Why? Because each lambda function refers to the variable x. x gets changed at every iteration through the loop, but they all still refer to the same object.
One way around this is to use a default argument. Default arguments are evaluated when the function is created, not when it is called.
alist = [lambda y=x: y for x in range(10) ]
print (alist[2]()) #2
print (alist[4]()) #4
(another way to do the same thing involves functools.partial which you'll see sometimes ...)
I often like to say -- "take care with closures". They can be a little tricky.

Related

Decorating on function defined within loop [duplicate]

This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 1 year ago.
I believe this is a similar case, however, I'm not sure how to apply the solutions to my case.
I am creating a function within a loop that is supplied by an argument. The function is called later, but it is stored for later and is supplied with the argument.
for type_str in ["type_1", "type_2", "type_3"]:
#decorator_exec.register(...)
def to_exec(num: int):
print(type_str, num)
# later...
# this is an example
i = 0
for func in decorator_exec.funcs:
func(i)
i++
Unfortunately, this is what would be produced:
type_3 0
type_3 1
type_3 2
I would like the output to ensure that the type_str variable (and any other variables that are included in to_exec() body), be what it should be when it is defined.
type_1 0
type_2 1
type_3 2
The to_exec() that is within the loop isn't used later within the loop, or outside the loop. It is solely stored by the decorator code and is called from its stored functions later.
If it isn't an XY problem, a possible solution would be to use a function factory (I'm not sure how that would work...)
You are not storing the functions for later, you are assigning three functions to the same name, so the last one is what sticks: the previous two get overwritten by the next one.
You can achieve what you're looking for with something like this:
# This is a dictionary created with a dict comprehension.
functions = {
# A decorator is just a function that takes a function and
# returns a function, so you can call it like a normal
# function. The first argument is your own function,
# and the result will be the decorated function.
type_str: decorator_exec.register(
# This is an anonymous function. You don't need to give
# a name to the function in this case because it will
# be called with the dict key it's stored in anyway.
lambda num: print(type_str, num),
..., # Any arguments the decorator needs
)
for type_str in ["type_1", "type_2", "type_3"]
}
This will create a dictionary with one entry per string in your list, such that functions["type_1"] will have the function defined with "type_1" and so on, which you can call, for example, like this: functions["type_2"](420).
You could achieve the same by adding each entry to an empty dict using a for loop, but comprehensions are usually more idiomatic. Because of the decorator, in this case it's probably more readable to do it the wordy way (and it allows you to use type annotations too):
functions = {}
for type_str in ["type_1", "type_2", "type_3"]:
#decorator_exec.register(...)
def the_func(num: int):
print(type_str, num)
functions[type_str] = the_func
The key takeaway is that you cannot use the same name more than once and expect different results, so you have to store your functions in a data structure. If you don't care about the name of the functions at all, you can use a list instead of a dictionary, and simply retrieve/call each function with its index.
You need to keep the reference to the correct string in memory. you can do that by accessing the items with an index variable.
The 2 first lines are relevant for this.
The rest is just to provide a working example,
def generate_funcs(xs, i=0):
return [lambda x: print(xs[i], x)] + generate_funcs(xs, i+1) if i<len(xs) else []
f = generate_funcs(["type_1", "type_2", "type_3"])
for (func, i) in zip(f, range(len(f))):
func(i)

How is Tkinter handling lambda after binding event?

I am trying to write some code that will send the value of an Entry box to a function based on a binding. I can technically get the behavior I want in the code below, but I'm a) not sure why it works and b) am quite sure I am not doing this in the most pythonic way. I'm pretty sure I'm misunderstanding the event or lambda or both.
In the code below the Entry box called input_box1 when the binding triggers, the inp_var1.get() code only gets the default value, not anything that has been entered into the box. In other words, the test1 function will print...
Test1 Foo
... no matter what you type into the entry.
The binding on input_box2 works exactly as I expect. I type anything in there and click somewhere else and it prints the new entry. However I don't understand why my lambda doesn't want an event or why I need to repeat the inp_var2.get() call.
If anyone knows what's going under the hood I'd love to hear it! Here's the code:
from tkinter import *
from tkinter import ttk
def test1(event, i):
print('Test1', i)
def test2(event, i):
print('Test2', i)
root = Tk()
title_label = Label(root, text='This does not work!')
title_label.grid(column=0, row=0)
inp_var1 = StringVar(value='Foo')
input_box1 = Entry(root, textvariable=inp_var1)
input_box1.grid(column=0, row=1)
inp_var2 = StringVar(value='Bar')
input_box2 = Entry(root, textvariable=inp_var2)
input_box2.grid(column=0, row=2)
input_box1.bind('<FocusOut>', lambda event, i=inp_var1.get(): test1(event, i))
input_box2.bind('<FocusOut>', lambda i=inp_var2.get(): test2(i, inp_var2.get()))
root.mainloop()
This has very little to do with Tkinter itself. It's also not so much connected to lambda as it is to Python in general.
Take both of those out of the equation, and consider the following Python program:
def f(x=3, y=4):
print('x =', x, 'y =', y)
f(0, 0)
f(0)
f()
Assuming Python 3 (or from __future__ import print_function), when run, this prints:
x = 0 y = 0
x = 0 y = 4
x = 3 y = 4
That's because the first call to f passes 0, 0, so x and y are both bound to zero. The second call to f passes just 0 so x is bound to 0 and y is bound to its default value of 4. The third call passes nothing at all and x and y are both bound to their default values.
(So far, this should all be clear enough.)
Now let's fuss with this a bit. I'll continue to assume Python 3 so that input means what in Python 2 we have to use raw_input to achieve:
def f(x=input('enter default x: '), y=input('enter default y: ')):
print('x =', x, 'y =', y)
print('about to call f three times')
f(0, 0)
f(0)
f()
Before you run this sample program, think about what you expect it to do. Then run it—here's my result:
$ python3 t.py
enter default x: hello
enter default y: world
about to call f three times
x = 0 y = 0
x = 0 y = world
x = hello y = world
Why did this read the default values for x and y before we even called it the first time? Why didn't it read new default values for x and y on each call?
Think about that for a bit, then read on
Really, do that. Ask why the inputs happened at these odd times.
Now that you've thought about it...
It did do that, though. That's the key here. The default values for your arguments are captured at the time the def statement is executed. The def statement, which binds the name f to our function, is actually run when Python loads the file, after seeing the body of the function. The function itself is run later, after Python has gotten to the print call and then to the first f call.
A lambda in Python is just a sort of anonymous function definition. Instead of:
def square(x):
return x * x
we can write:
square = lambda x: x * x
The lambda expression defines a new function-like item, a lambda function—you can't use if/else type statements inside one of these, just expressions, and they automatically return the value of their expression. In this case our lambda function has one argument named x. The function returns x * x.
The outer assignment, square =, binds this lambda function to the name square, just as if we'd done def square(x) instead. So it's mostly just a syntactic trick: we can have a real function, like square, with arguments, or a limited lambda function that can only use expressions, like this anonymous function that we almost immediately bind to the name square.
The arguments part works the same either way though. Just as with f and input, if we bind x:
square = lambda x=3: x * x
or:
square = lambda x=int(input('choose a default for x now> ')): x * x
that happens just once, when the lambda expression itself executes. The function now has a variable x with a default value.
When, later, we call the function, we can provide a value for x, or let it default. If we don't provide a value, Python uses the default that it captured earlier, when we executed the line with lambda in it, rather than now, when we call the lambda function.
This all holds for Tkinter as well. You wrote:
input_box2.bind('<FocusOut>', lambda i=inp_var2.get(): test2(i, inp_var2.get()))
but that's pretty much the same as writing:
def f(i=inp_var2.get()):
test2(i, inp_var2.get())
input_box2.bind('<FocusOut>', f)
except that you don't have to come up with the function-name f here. The lambda variant of the function has no name, but it's still just a function. (For that matter, when we do square = lambda ..., the lambda function doesn't have a name. The name square is the name of a variable, not the name of the function. The variable is merely bound to the function, so that square(10) calls it.)
Anyway, later, when Tkinter has an event that matches <FocusOut>, Tkinter calls f ... or, if you used lambda, calls your unnamed lambda function. Tkinter provides one argument to that function; the one argument it provides is the event. So your default value for i in f above is irrelevant—you could do:
def f(i=None):
test2(i, inp_var2.get())
or:
def f(i='hello world'):
test2(i, inp_var2.get())
because when Tkinter calls f, it always provides an actual argument for i.

Passing some values as variables

I'm a physics graduate student with some basic knowledge of Python and I'm facing some problems that challenge my abilities.
I'm trying to pass some variables as dummies and some not. I have a function that receives a function as the first argument, but I need that some values to be declared "a posteriori".
What I'm trying to mean is the following:
lead0 = add_leads(lead_shape_horizontal(W, n), (0, 0, n), sym0)
The function "add_leads" takes some function as well as a tuple and a third argument which is fine. But n hasn't any definition yet. I want that n has an actual sense when it enters "add_leads".
Here is the actual function add_leads
def add_leads(shape, origin_2D, symm):
lead_return = []
lead_return_reversed = []
for m in range(L):
n = N_MIN + m
origin_3D = list(origin_2D)+[n]
lead_return.append(kwant.Builder(symm))
lead_return[m][red.shape(shape(n), tuple(origin_3D))] = ONN + HBAR*OMEGA*n
lead_return[m][[kwant.builder.HoppingKind(*hopping) for
hopping in hoppings_leads]] = HOPP
lead_return[m].eradicate_dangling()
Note that n is defined under for, so, I wish to put the value of n in shape(n) (in this case leads_shape_horizontal with a fixed value for W, not for n).
I need this to be this way because eventually the function which is the argument for lead_shape might have more than 2 input values but still just need to vary n
Can I achieve this in Python? If I can, How to do so?
Help will be really appreciated.
Sorry for my english!
Thanks in advance
You probably should pass in the function lead_shape_horizontal, not the function with argument lead_shape_horizontal(W, n)
Because the latter one will return the result of the function, not function object itself. Unless the return value is also a function, you'll get an error when you later call shape(n), which is identical to lead_shape_horizontal(W, n)(n)
As for providing a fix value for W but not for n, you can either give W a default value in the function or just don't make it an argument
For example,
def lead_shape_horizontal(n, W=some_value):
# do stuff
or If you always fix W, then it doesn't have to be an argument
def lead_shape_horizontal(n):
W = some_value
# do stuff
Also note that you didn't define n when calling function, so you can't pass in n to the add_leads function.
Maybe you have to construct the origin_2D inside the function
like origin_2D = origin_2D + (n,)
Then you can call the function like this lead0 = add_leads(lead_shape_horizontal, (0, 0), sym0)
See Python Document to understand how default value works.
Some advice: Watch out the order of arguments when you're using default value.
Also watch out when you're passing in mutable object as default value. This is a common gotcha

list of functions with parameters

I need to obtain a list of functions, where my function is defined as follows:
import theano.tensor as tt
def tilted_loss(y,f,q):
e = (y-f)
return q*tt.sum(e)-tt.sum(e[e<0])
I attempted to do
qs = np.arange(0.05,1,0.05)
q_loss_f = [tilted_loss(q=q) for q in qs]
however, get the error TypeError: tilted_loss() missing 2 required positional arguments: 'y' and 'f'. I attempted the simpler a = tilted_loss(q=0.05) with the same result.
How do you go about creating this list of functions when parameters are required? Similar questions on SO consider the case where parameters are not involved.
You can use functools.partial:
q_loss_f = [functools.partial(tilted_loss, q=q) for q in qs]
There are 2 ways you can solve this problem. Both ways require you know the default values for y and f.
With the current function, there's simply no way for the Python interpreter to know the value of y and f when you call tilted_loss(q=0.05). y and f are simply undefined & unknown.
Solution (1): Add default values
We can fix this by adding default values for the function, for example, if default values are: y = 0, f = 1:
def tilted_loss(q, y=0, f=1):
# original code goes here
Note that arguments with default values have to come AFTER non-default arguments (i.e q).
Solution (2): Specify default values during function call
Alternatively, just specify the default values every time you call that function. (Solution 1 is better)

Python's lambda with underscore for an argument?

What does the following code do?
a = lambda _:True
From what I read and tested in the interactive prompt, it seems to be a function that returns always True.
Am I understanding this correctly? I hope to understand why an underscore (_) was used as well.
The _ is variable name. Try it.
(This variable name is usually a name for an ignored variable. A placeholder so to speak.)
Python:
>>> l = lambda _: True
>>> l()
<lambda>() missing 1 required positional argument: '_'
>>> l("foo")
True
So this lambda does require one argument. If you want a lambda with no argument that always returns True, do this:
>>> m = lambda: True
>>> m()
True
Underscore is a Python convention to name an unused variable (e.g. static analysis tools does not report it as unused variable). In your case lambda argument is unused, but created object is single-argument function which always returns True. So your lambda is somewhat analogous to Constant Function in math.
it seems to be a function that returns True regardless.
Yes, it is a function (or lambda) that returns True. The underscore, which is usually a placeholder for an ignored variable, is unnecessary in this case.
An example use case for such a function (that does almost nothing):
dd = collections.defaultdict(lambda: True)
When used as the argument to a defaultdict, you can have True as a general default value.
Below is the line of code in question:
a = lambda _:True
It creates a function having one input parameter: _. Underscore is a rather strange choice of variable name, but it is just a variable name. You can use _ anywhere, even when not using lambda functions. For example, instead of....
my_var = 5
print(my_var)
You could write:
_ = 5
print(_)
However, there was a reason that _ was used as the name of parameter name instead of something like x or input. We'll get to that in a moment.
First, we need to know that the lambda-keyword constructs a function, similar to def, but with different syntax. The definition of the lambda function, a = lambda _:True, is similar to writing:
def a(_):
return True
It creates a function named a with an input parameter _, and it returns True. One could have just as easily written a = lambda x:True, with an x instead of an underscore. However, the convention is to use _ as a variable name when we do not intend to use that variable. Consider the following:
for _ in range(1, 11):
print('pear')
Notice that the loop index is never used inside of the loop-body. We simply want the loop to execute a specified number of times. As winklerrr has written, "the variable name _ is [...] like a "throw-away-variable", just a placeholder which is of no use. "
Likewise, with ``a = lambda x:True the input parameter is not used inside the body of the function. It does not really matter what the input argument is, as long as there is one. The author of that lambda-function wrote _ instead of something like x, to indicate that the variable would not be used.
Note that the lambda does have an argument; So, writing
a(), will raise an error.
If you want a lambda with no argument write something like this:
bar = lambda: True
Now calling bar(), with no args, will work just fine.
A lambda which takes no arguments need not always return the same value:
import random
process_fruit = lambda : random.random()
The lambda function above is more complex that just a something which always returns the same constant.
One reason that programmers sometimes us the lambda keyword instead of def is for functions which are especially short and simple. Note that a lambda definition can usually fit all on one line, whereas, it is difficult to do the same with a def statement. Another reason to use lambda instead of def sf when the function will not be used again. If we don't want to call the function again later, then there is no need to give the function a name. For example consider the following code:
def apply_to_each(transform, in_container):
out_container = list()
for idx, item in enumerate(container, 0):
out_container[idx] = transform(item)
return out_container
Now we make the following call:
squares = apply_to_each(lambda x: x**2 range(0, 101))
Notice that lambda x: x**2 is not given a label. This is because we probably won't call it again later, it was just something short and simple we needed temporarily.
The fact that lambda functions need not be given a name is the source of another name to describe them: "anonymous functions."
Also note that lambda-statements are like a function-call in that they return a reference to the function they create. The following is illegal:
apply_to_each(def foo(x): x**2 , range(0, 101))
Whereas, apply_to_each(lambda x: x**2 range(0, 101)) is just fine.
So, we use lambda instead of def and _ instead of a long variable name when we want something short, sweet and probably won't want use again later.
Lambda means a function.
The above statement is same as writing
def f(_):
return True
For lambda a variable needs to be present. So you pass it a variable called _(Similarly you could pass x, y..)
Underscore _ is a valid identifier and is used here as a variable name. It will always return True for the argument passed to the function.
>>>a('123')
True

Categories