lambda in for loop only takes last value [duplicate]
лямбда-код в цикле for принимает только последнее значение
У меня есть код
options=["INFO", "WARNING", "DEBUG"]
for i inrange(len(options)): option=options[i] __cMenu.add_command( label="{}".format(option), command=lambda: self.filter_records(column, option) )
который создает и сохраняет несколько лямбд, которые должны фиксировать разные значения локальной переменной option. Однако, когда эти лямбды используются, все они ведут себя так, как если бы options было установлено значение "DEBUG", последнее значение, которое оно принимает в цикле.
Я предполагаю, что это как-то связано со сборкой мусора, поскольку остается только последний вариант, но я не могу понять, как этого избежать.
Переведено автоматически
Ответ 1
Пожалуйста, прочтите о минимальных примерах. Я полагаю, что, не читая свой код, вы столкнулись с хорошо известной проблемой, рассмотренной в предыдущих вопросах и ответах, для иллюстрации которой требуется 2 строки. Имена в телах функций вычисляются при выполнении функции.
funcs = [lambda: i for i inrange(3)] for f in funcs: print(f())
выводит '2' 3 раза, потому что 3 функции идентичны и 'i' в каждой не вычисляется до вызова, когда i == 2. Однако,
funcs = [lambda i=i:i for i inrange(3)] for f in funcs: print(f())
создает три разные функции, каждая с разным записанным значением, поэтому выводятся 0, 1 и 2. В вашем заявлении
чтобы отличать переменную цикла от параметра функции. Если column изменено внутри цикла, потребуется такая же обработка.
Ответ 2
Замыкания в Python фиксируют переменные, а не значения. Например, рассмотрим:
deff(): x = 1 g = lambda : x x = 2 return g()
Каким вы ожидаете результата вызова f() быть? Правильный ответ - 2, потому что лямбда-код f захватил переменнуюx, а не ее значение 1 на момент создания.
Теперь, если, например, мы напишем:
L = [(lambda : i) for i inrange(10)]
мы создали список из 10 различных лямбд, но все они фиксировали одну и ту же переменную i, таким образом, при вызове L[3]() результатом будет 9, потому что значение переменной i в конце итерации было 9 (в Python a comprehension не создает новую привязку для каждой итерации; он просто продолжает обновлять ту же привязку).
"Трюк", который часто можно увидеть в Python при захвате значения, являющегося желаемой семантикой, заключается в использовании аргументов по умолчанию. В Python, в отличие, скажем, от C ++, выражения значений по умолчанию вычисляются во время определения функции (т.Е. При создании лямбда-выражения), а не при вызове функции. Итак, в коде типа:
L = [(lambda j=i: j) for i inrange(10)]
мы объявляем параметр j и устанавливаем по умолчанию текущее значение i на момент создания лямбды. Это означает, что при вызове, например, L[3]() на этот раз результатом будет 3 из-за значения параметра "hidden" по умолчанию (вызов L[3](42), конечно, вернет 42).
Чаще вы видите явно более запутанную форму
lambda i=i: ...
где "скрытый" параметр имеет то же имя, что и переменная, значение которой мы хотим получить.
Ответ 3
Я знаю, что опаздываю, но я нашел грязный обходной путь, который выполняет работу (протестировано в Python 3.7)
Если вы используете двойную лямбду (как я уже сказал, очень неаккуратно), вы можете сохранить значение, например:
Шаг 1: Создайте вложенный оператор lambda:
send_param = lambda val: lambda: print(val)
Шаг 2: Используйте оператор lambda:
send_param(i)
send_param Метод возвращает самую внутреннюю лямбду (lambda: print(val)) без выполнения инструкции, пока вы не вызовете результат send_param который не принимает аргументов, например:
a = send_param(i) a()
Только вторая строка будет выполнять оператор print.