49. Что выведет код с замыканием в списке лямбда-функций
Условие задачи:
Что выведет код?
def multipliers():
return [lambda x : i * x for i in range(4)]
print [[m(2) for m in multipliers()]
Спойлеры к решению
Подсказки
- В коде используется список lambda-функций.
- Каждая lambda использует переменную
iиз внешней области видимости. - Значение
iне фиксируется в момент создания lambda. - Все lambda-функции будут смотреть на одно и то же последнее значение
i. - После завершения
range(4)последнее значениеiбудет3. - Поэтому каждая функция фактически посчитает
3 * x.
Решение
В исходном виде код записан с синтаксической ошибкой:
print [[m(2) for m in multipliers()]
Корректный вариант для Python 3:
def multipliers():
return [lambda x: i * x for i in range(4)]
print([m(2) for m in multipliers()])
Результат:
[6, 6, 6, 6]
Почему так происходит:
return [lambda x: i * x for i in range(4)]
Может показаться, что создаются такие функции:
lambda x: 0 * x
lambda x: 1 * x
lambda x: 2 * x
lambda x: 3 * x
Но на самом деле все lambda-функции запоминают не значение i, а ссылку на переменную i.
После завершения цикла последнее значение i равно 3. Поэтому каждая lambda работает так:
lambda x: 3 * x
Когда вызывается:
m(2)
каждая функция возвращает:
3 * 2 = 6
Поэтому итоговый список:
[6, 6, 6, 6]
Чтобы получить ожидаемый результат [0, 2, 4, 6], нужно зафиксировать текущее значение i в аргументе по умолчанию:
def multipliers():
return [lambda x, i=i: i * x for i in range(4)]
print([m(2) for m in multipliers()])
Результат:
[0, 2, 4, 6]
Здесь i=i сохраняет текущее значение i на каждой итерации, поэтому каждая lambda получает своё собственное значение.