Недавно я начал играть с 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
т.д.