Проблема, с которой вы здесь сталкиваетесь, заключается в различии между «ранним связыванием» и «поздним связыванием».
Когда Python ищет переменную из внешней области видимости (в данном случае i
), он используетПозднее связывание.Это означает, что он видит значение этой переменной в тот момент, когда функция называется , а не значение во время определения функции.
Итак, в вашем примере кода все 10Лямбда-функции видят окончательное значение, присвоенное переменной i
в циклическом процессе: 9
.
Ответ Грега показывает один из способов принудительного раннего связывания (т. е. создать дополнительное замыкание и вызвать его немедленно, пока ещевнутри цикла).
Другим часто используемым подходом к форсированию семантики раннего связывания является «взлом аргумента по умолчанию», который связывает переменную в качестве аргумента по умолчанию во время определения функции:
>>> fs = [(lambda x, _i=i: x + _i) for i in xrange(10)]
>>> [f(0) for f in fs]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Любой метод работает.Преимущество Грега состоит в том, что он не портит сигнатуру возвращаемой функции: хак с аргументами по умолчанию работает быстрее и значительно удобнее для чтения, чем добавление дополнительного уровня закрытия при определении именованных функций вместо использования лямбда-выражений.