This question already has an answer here:
这个问题在这里已有答案:
- Passing argument to a function via a Button in Tkinter, starnge behaviour in loop 1 answer
通过Tkinter中的Button将参数传递给函数,循环1中的starnge行为回答
I have searched deeply for a solution to this problem, but, being new to Python and tkinter, it is difficult to find solutions to problems in a language you are largely unfamiliar with. I hope this is not a repeat question.
我已经深入搜索了这个问题的解决方案,但是,对于Python和tkinter来说,很难找到一种你很不熟悉的语言问题的解决方案。我希望这不是一个重复的问题。
Suppose I create a series of buttons in tkinter, each of which will sequentially display a letter of the alphabet, defined as such:
假设我在tkinter中创建了一系列按钮,每个按钮将依次显示字母表的字母,定义如下:
from string import ascii_uppercase as alphabet
from tkinter import Button
blockLetterButtons = []
for i in range(0,26):
blockLetterButtons.append(Button(competitorSelectionFrame,
text=alphabet[i], command=some_function(alphabet[i])))
Somewhere below this in the for
loop is a .grid(...)
for all of these.
for循环中低于此值的地方是所有这些的.grid(...)。
The function that is being referenced in the command
attribute is as the following:
命令属性中引用的函数如下所示:
def some_function(letter):
print(letter)
So I recognize two problems with my code:
所以我认识到我的代码有两个问题:
- The function in the
command
attribute is executing immediately upon running the code, because I am specifying a function call rather than a function name. - Even if that was the proper way to call the function with a parameter, using
alphabet[i]
wouldn't work, because it would pass whatever the value ofi
is upon click, and not whateveri
was upon execution of thefor
loop.
命令属性中的函数在运行代码时立即执行,因为我指定的是函数调用而不是函数名。
即使这是使用参数调用函数的正确方法,使用alphabet [i]也行不通,因为它会在点击时传递i的任何值,而不是在执行for循环时我所做的任何事情。
So my questions are the following:
所以我的问题如下:
- How can I specify a function in the
command
attribute and pass to that function with a parameter? - How can I specify that parameter to be the letter of the alphabet specific to that button, without defining a button explicitly for every letter?
如何在命令属性中指定一个函数并使用参数传递给该函数?
如何将该参数指定为特定于该按钮的字母表的字母,而不是为每个字母明确定义按钮?
I am fairly certain I could use a list comprehension to solve the second problem, perhaps a dictionary with the key equal to the letter and the value equal to the button, but then I still wouldn't know how to get the key value from the button on its own.
我相当肯定我可以使用列表理解来解决第二个问题,也许是一个字母,其键等于字母,值等于按钮,但后来我仍然不知道如何从键中获取键值按钮本身。
2 个解决方案
#1
0
You should use a higher order function (a function that returns a function). To demonstrate, I will not use tkinter but the principle is the same:
您应该使用更高阶函数(返回函数的函数)。为了演示,我不会使用tkinter,但原理是相同的:
In [25]: def some_func_maker(letter):
...: def some_func():
...: print(letter)
...: return some_func
...:
In [26]: buttons = []
In [27]: from string import ascii_uppercase as alphabet
In [28]: for i in range(26):
...: buttons.append(some_func_maker(alphabet[i]))
...:
In [29]: for b in buttons: b()
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
Note, you can inline this if you don't want to keep a some_func_maker
around using a lambda:
注意,如果你不想使用lambda来保持some_func_maker,你可以内联这个:
In [37]: buttons = []
...: for c in alphabet:
...: buttons.append(
...: (lambda x: lambda: some_function(x))(c)
...: )
...:
In [38]: for b in buttons: b()
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
A common trick is that is less verbose is to use a default-argument with your lambda, however, I consider this more hacky, although it is quite common in Python:
一个常见的技巧是使用lambda的默认参数更简洁,但是,我认为这更加hacky,尽管它在Python中很常见:
In [43]: buttons = []
...: for c in alphabet:
...: buttons.append(
...: lambda x=c: some_function(x)
...: )
...:
In [44]: buttons[0]()
A
In [45]: buttons[1]()
B
In [46]: buttons[-1]()
Z
Note also, it is consider an antipattern to use for i in range(n): some_iterable[i]
when you can simply iterate over the iterable instead. Usually, you just want the actual value, so you just do for x in some_iterable
.
另请注意,考虑在范围(n)中使用i的反模式:some_iterable [i]当您可以简单地迭代迭代时。通常,您只需要实际值,因此您只需要在some_iterable中执行x。
#2
0
Currying is the practice of passing some arguments to a function to produce a new function which can be called later but "remembers" its earlier arguments. You can use this technique to pass the state.
Currying是将一些参数传递给函数以生成一个新函数的做法,该函数可以在以后调用,但“记住”其早期的参数。您可以使用此技术来传递状态。
def some_function(letter):
def f():
print(letter)
return f
...
command = some_function(alphabet[i])
So some_function(alphabet[i])()
would call the function, but only using one set of parentheses simply stores the value for later.
所以some_function(alphabet [i])()会调用该函数,但只使用一组括号只是为了以后存储该值。
#1
0
You should use a higher order function (a function that returns a function). To demonstrate, I will not use tkinter but the principle is the same:
您应该使用更高阶函数(返回函数的函数)。为了演示,我不会使用tkinter,但原理是相同的:
In [25]: def some_func_maker(letter):
...: def some_func():
...: print(letter)
...: return some_func
...:
In [26]: buttons = []
In [27]: from string import ascii_uppercase as alphabet
In [28]: for i in range(26):
...: buttons.append(some_func_maker(alphabet[i]))
...:
In [29]: for b in buttons: b()
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
Note, you can inline this if you don't want to keep a some_func_maker
around using a lambda:
注意,如果你不想使用lambda来保持some_func_maker,你可以内联这个:
In [37]: buttons = []
...: for c in alphabet:
...: buttons.append(
...: (lambda x: lambda: some_function(x))(c)
...: )
...:
In [38]: for b in buttons: b()
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
A common trick is that is less verbose is to use a default-argument with your lambda, however, I consider this more hacky, although it is quite common in Python:
一个常见的技巧是使用lambda的默认参数更简洁,但是,我认为这更加hacky,尽管它在Python中很常见:
In [43]: buttons = []
...: for c in alphabet:
...: buttons.append(
...: lambda x=c: some_function(x)
...: )
...:
In [44]: buttons[0]()
A
In [45]: buttons[1]()
B
In [46]: buttons[-1]()
Z
Note also, it is consider an antipattern to use for i in range(n): some_iterable[i]
when you can simply iterate over the iterable instead. Usually, you just want the actual value, so you just do for x in some_iterable
.
另请注意,考虑在范围(n)中使用i的反模式:some_iterable [i]当您可以简单地迭代迭代时。通常,您只需要实际值,因此您只需要在some_iterable中执行x。
#2
0
Currying is the practice of passing some arguments to a function to produce a new function which can be called later but "remembers" its earlier arguments. You can use this technique to pass the state.
Currying是将一些参数传递给函数以生成一个新函数的做法,该函数可以在以后调用,但“记住”其早期的参数。您可以使用此技术来传递状态。
def some_function(letter):
def f():
print(letter)
return f
...
command = some_function(alphabet[i])
So some_function(alphabet[i])()
would call the function, but only using one set of parentheses simply stores the value for later.
所以some_function(alphabet [i])()会调用该函数,但只使用一组括号只是为了以后存储该值。