Лямбды и захват переменных в Python

Лямбды и захват переменных в Python

@python_quiz

Разберем этот квиз

Коротко: при создании лямбд в цикле важно понимать, когда именно переменная, на которую они ссылаются, фиксируется. В приведенном примере одна группа лямбд захватывает переменную по ссылке (late binding), другая — фиксирует значение через значение по умолчанию аргумента.

Исходный код

python

Что происходит (пошагово)

  • При выполнении [lambda x: i*x for i in range(3)] в каждую лямбда-функцию записывается не текущее числовое значение i, а ссылка на переменную i из внешней области видимости. Эта переменная изменяется циклом и после завершения цикла равна 2. При последующем вызове всех лямбд i будет равно 2, поэтому каждая из них вернёт 2*5 = 10. Получаем [10, 10, 10].
  • При использовании lambda x, i=i: i*x значение i передаётся как значение по умолчанию аргумента i в момент создания лямбды. Это фиксирует текущее значение i (0, затем 1, затем 2). При вызове каждое замкнутое значение использует свою зафиксированную копию: [0*5, 1*5, 2*5] → [0, 5, 10].

Демонстрация (ожидаемый вывод)

python

Анализ вариантов ответа (кратко)

  • [[10, 10, 10], [0, 5, 10]] — совпадает с реальным выводом программы (объяснение выше).
  • [[0, 5, 10], [0, 5, 10]] — подразумевает, что обе группы лямбд зафиксировали i при создании; это неверно для первой группы.
  • Произойдет ошибка — в данном коде ошибок не возникает.
  • [[10, 10, 10], [10, 10, 10]] — такое поведение случилось бы, если бы и в первой, и во второй группе использовалась поздняя привязка к одному и тому же итоговому значению i; однако вторая группа специально фиксирует значение через i=i.

Альтернативные способы решения и паттерны

  • Фабрика функций (функция-генератор замыканий):
python
  • Использование functools.partial или operator:
python
  • Явно передавать значение в лямбду как аргумент по умолчанию (уже в примере) — самый компактный и распространённый приём.

Выводы

  • Лямбды захватывают переменные по ссылке; значение переменной будет взято при вызове, если не зафиксировать его явно.
  • Для фиксации текущего значения в момент создания замыкания удобно использовать аргумент со значением по умолчанию (i=i) или фабрику функций.
  • Понимание механики захвата переменных помогает избежать тонких багов при создании списка функций в цикле.

Report Page