如何将特定于对象的值传递给循环中创建的对象的函数? [重复]

时间:2022-05-18 21:10:11

This question already has an answer here:

这个问题在这里已有答案:

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:

所以我认识到我的代码有两个问题:

  1. 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.
  2. 命令属性中的函数在运行代码时立即执行,因为我指定的是函数调用而不是函数名。

  3. 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 of i is upon click, and not whatever i was upon execution of the for loop.
  4. 即使这是使用参数调用函数的正确方法,使用alphabet [i]也行不通,因为它会在点击时传递i的任何值,而不是在执行for循环时我所做的任何事情。

So my questions are the following:

所以我的问题如下:

  1. How can I specify a function in the command attribute and pass to that function with a parameter?
  2. 如何在命令属性中指定一个函数并使用参数传递给该函数?

  3. 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?
  4. 如何将该参数指定为特定于该按钮的字母表的字母,而不是为每个字母明确定义按钮?

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])()会调用该函数,但只使用一组括号只是为了以后存储该值。