Недавно я начал играть с Python и обнаружил что-то необычное в работе замыканий. Рассмотрим следующий код:
adders=[0,1,2,3]
for i in [0,1,2,3]:
adders[i]=lambda a: i+a
print adders[1](3)
Он создает простой массив функций, которые принимают один вход и возвращают этот вход, добавленный числом. Функции построены в forцикле, где итератор iработает от 0до 3. Для каждого из этих чисел создается lambdaфункция, которая захватывает iи добавляет ее к входу функции. Последняя строка вызывает вторую lambdaфункцию с 3параметром. К моему удивлению, результат был 6.
Я ожидал 4. Я рассуждал так: в Python все является объектом, поэтому каждая переменная является указателем на него. При создании lambdaзамыканий для iя ожидал, что он будет хранить указатель на целочисленный объект, на который в данный момент указывает i. Это означает, что при iназначении нового целочисленного объекта это не должно влиять на ранее созданные замыкания. К сожалению, проверка addersмассива в отладчике показывает, что это так. Все lambdaфункции относятся к последнему значению i, 3, что приводит к adders[1](3)возвращению 6.
Что заставляет меня задуматься о следующем:
- Что именно запечатлевают крышки?
- Каков наиболее элегантный способ убедить
lambdaфункции захватить текущее значениеiтаким образом, чтобы это не повлияло приiизменении его значения?
iпокинуть пространство имен?
print iэто не сработает после цикла. Но я проверил это для себя, и теперь я понимаю, что вы имеете в виду - это работает. Я понятия не имел, что переменные цикла задерживаются после тела цикла в python.
if, with, и tryт.д.