Как передать в lambda-функцию текущее значение переменной?
Писал простенький калькулятор в tkinter. Некоторые кнопки инициализировал в цикле:
for i in range(10):
button = Button(command=lambda: some_function(i), ...)
Проблема в том, что лямбда функции запоминают не текущее значение переменной, а ее адрес. То есть, после окончания цикла я получу десять кнопок, вызывающих одну и ту же функцию с одним и тем же аргументом (some_function(9)). Как можно обойти эту особенность, передав значения переменной i (чтобы кнопки вызывали функции: some_function(1), some_function(2) и т. д.) ?
Вот, как это выглядит сейчас:
from tkinter import *
def enter_digit(number):
print(number)
root = Tk()
for i in range(9):
button_column, button_row = i % 3, i // 3
button = Button(root, text=str(i+1), command=lambda: enter_digit(i + 1))
button.grid(column=button_column, row=button_row)
root.mainloop()
Ответы (3 шт):
Вы можете сделать отдельную функцию, которая будет возвращать нужную вам лямбда-функцию. То, как это работает у вас:
>>> l = []
>>> for i in range(10):
... l += [lambda: str(i)]
...
>>> l[0]()
'9'
>>> l[5]()
'9'
>>> l[9]()
'9'
Как это можно решить:
>>> def wrap(function, *args):
... return lambda: function(*args)
...
>>> l = []
>>> for i in range(10):
... l += [wrap(str, i)]
...
>>> l[0]()
'0'
>>> l[5]()
'5'
>>> l[9]()
'9'
Это обычное поведение замыканий в питон. Так как все лямбды создаются в одной и то й же области видимости, то они все указывают на одну и ту же переменную (счетчик цикла i в данном случае).
Обойти можно несколькими способами. Первый - это создавать лямбду в фукнции:
def createCommand(i):
return lambda: some_function(i)
...
for i in range(10):
button = Button(commnand=createCommand(i), ...)
Второй, более элегантный как по мне, вариант, это создать параметр со значением по-умолчанию:
for i in range(10):
button = Button(command=lambda i=i: some_function(i), ...)
Тут используется тот факт, что значение по умолчанию вычисляется в момент определения функции или лябмды.
Попробуйте так:
from tkinter import *
def enter_digit(number, buf=0): # buf ???
print(number)
root = Tk()
for i in range(9):
button_column, button_row = i % 3, i // 3
# vvvvv v
button = Button(root, text=str(i+1), command=lambda i=i+1: enter_digit(i))
button.grid(column=button_column, row=button_row)
root.mainloop()
