Почему лямбды, созданные в цикле, возвращают одно и то же значение?
- Что такое позднее связывание в замыканиях Python?
Замыкание захватывает переменную, а не её значение: значение читается в момент вызова. К моменту вызова цикл закончился, и все лямбды видят последнее значение i. Исправление — зафиксировать значение: аргумент по умолчанию (lambda i=i: i) или functools.partial.
- [lambda: i for i in range(3)] → все возвращают 2
- Значения по умолчанию вычисляются при определении функции — здесь это и нужно
- Та же проблема с колбэками в цикле (кнопки GUI, обработчики)
А ещё Отличие от Java: там лямбда захватывает значение effectively final переменной
Подробно
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # [2, 2, 2]
funcs = [lambda i=i: i for i in range(3)]
print([f() for f in funcs]) # [0, 1, 2]
from functools import partial
funcs = [partial(lambda x: x, i) for i in range(3)]
Почему. Замыкание хранит ссылку на ячейку (cell) переменной внешней области. Все лямбды ссылаются на одну ячейку i; при вызове каждая читает её текущее значение.
Это связывание по имени — то же правило LEGB, что и для обычных функций. Связь с ловушкой изменяемого аргумента по умолчанию обратная: там «раннее» вычисление по умолчанию мешает, здесь — помогает.
Что хотят услышать
- Захват переменной, а не значения.
- Два способа исправить.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Замыкание видит переменную, а не значение; лечится через i=i или partial
- ✗Это ошибка интерпретатора, исправленная в Python 3.12: в новых версиях лямбды сохраняют значение i
- ✗Лямбды копируют значение i в момент создания, а одинаковый результат даёт кэш интерпретатора
- ✗Нужно объявить i как global внутри лямбды, тогда каждая получит своё значение
Разбор темы
- Функции — аргументы, области видимости, замыкания и декораторы
Как передаются аргументы, *args и **kwargs, LEGB, ловушка изменяемого значения по умолчанию, замыкания и позднее связывание, декораторы и functools.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.