Замыкания в Python: lambda в цикле и default-аргументы

Замыкания в Python: lambda в цикле и default-аргументы

@python_quiz

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

Дана небольшая программа на Python — набор лямбд создаётся в цикле, а затем вызывается:

python

Что здесь важно понять и почему программа печатает ожидаемый список.

Ключевая идея: когда фиксируется значение переменной

В Python замыкание (lambda или вложенная функция) захватывает переменные окружения по ссылке. Это означает, что если лямбда просто использует переменную цикла (без хитростей), она будет смотреть на её текущее значение во время вызова — а не на значение в момент создания лямбды. Это называется поздним связыванием (late binding).

Но в примере выше используется приём: default-аргумент i=i. Значение справа вычисляется прямо при создании каждой лямбды и сохраняется как значение параметра по умолчанию. Таким образом, каждое замыкание получает собственную фиксированную копию значения i.

Результат выполнения программы:

python

Демонстрация контраста: без default-аргумента

Чтобы увидеть эффект позднего связывания, сравним с вариантом без i=i:

python

Здесь все лямбды ссылаются на одну и ту же переменную i, и к моменту вызова она равна 2 (последнее значение в цикле), поэтому получаем три раза 2.

Разбор возможных предположений (вариантов ответа)

  • Идея, что каждая лямбда сохранила текущее i через default-аргумент — именно так и работает: при создании каждого lambda справа i вычисляется и становится значением аргумента по умолчанию.
  • Идея о том, что лямбды всегда захватывают i по ссылке и поэтому получится три одинаковых значения — справедлива для случая без default-аргумента. Тут важно различать два сценария: с и без фиксации значения при создании.
  • Версия с [0, 0, 0] не соответствует механике: переменная i не "обнуляется" после создания лямбд; такое поведение не следует из кода.
  • Ошибка времени выполнения из‑за замыкания — тоже не релевантна: замыкания корректны и ошибок не вызовут в этом примере.

Паттерны и альтернативы

  1. Default-аргумент (простой и читаемый способ)
python
  1. Вложенная функция с параметром (эквивалентно default-аргументу)
python
  1. functools.partial для передачи значения
python

Выбирайте тот вариант, который лучше читается в вашем коде. Default-аргумент — краткий и широко используемый трюк.

Вывод

При создании замыканий в цикле важно понимать момент привязки значений. Если нужно зафиксировать текущее значение переменной для каждой функции, применяйте фиксацию в момент создания (например, через i=i в lambda или через фабрику функций). Иначе вы столкнётесь с эффектом позднего связывания и неожидаемыми одинаковыми значениями при вызове.

Report Page