Недавно я начал играть с 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
.
Я ожидал a 4
. Мои рассуждения были: в Python все является объектом, и поэтому каждая переменная имеет важное значение для указателя на него. При создании закрытий lambda
для i
я ожидал, что он сохранит указатель на целочисленный объект, на который в данный момент указывает i
. Это означает, что когда i
назначается новый целочисленный объект, он не должен влиять на ранее созданные закрытия. К сожалению, проверка массива adders
в отладчике показывает, что это так. Все функции lambda
относятся к последнему значению i
, 3
, что приводит к возврату adders[1](3)
6
.
Что заставляет меня задуматься о следующем:
- Что точно фиксируют замыкания?
- Каков самый элегантный способ убедить функции
lambda
для захвата текущего значенияi
таким образом, чтобы это не повлияло, когдаi
изменит его значение?