Как передать в 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'
→ Ссылка
Автор решения: Roman Konoval

Это обычное поведение замыканий в питон. Так как все лямбды создаются в одной и то й же области видимости, то они все указывают на одну и ту же переменную (счетчик цикла 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), ...) 

Тут используется тот факт, что значение по умолчанию вычисляется в момент определения функции или лябмды.

→ Ссылка
Автор решения: S. Nick

Попробуйте так:

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()

введите сюда описание изображения

→ Ссылка