Дан фрагмент на Python: funcs = [lambda x: i*x for i in range(5)]; вызов funcs[3](2) возвращает 8, объясните, почему происходит «позднее связывание» переменной i, какие варианты исправления существуют и как поведение изменится в разных версиях Python

26 Мая в 08:51
11 +1
0
Ответы
1
Коротко — потому что лямбда-замыкания захватывают имя, а не текущее значение; после цикла имя iii равно последнему значению цикла (444), поэтому все функции используют это значение и вызов `funcs[3](2)` даёт 4⋅2=84\cdot 2 = 842=8.
Почему так происходит
- Выражение `lambda x: i*x` замыкает переменную `i` (ссылку на неё). Значение `i` вычисляется только при вызове лямбды, а не при её создании.
- При `range(5)` значения 0,1,2,3,40,1,2,3,40,1,2,3,4; после завершения цикла `i == 4`, и все лямбды умножают на 444.
Варианты исправления (обычные приёмы)
1) Привязать значение в аргументе по умолчанию:
```
funcs = [lambda x, i=i: i*x for i in range(5)]
```
Тогда `funcs[3](2)` вернёт 3⋅2=63\cdot 2 = 632=6.
2) Использовать функцию-фабрику замыкания:
```
def make(i):
return lambda x: i*x
funcs = [make(i) for i in range(5)]
```
Результат: `funcs[3](2)` → 666.
3) Использовать functools.partial или operator:
```
from functools import partial
import operator
funcs = [partial(operator.mul, i) for i in range(5)]
```
`funcs[3](2)` → 666.
4) Немного более явное немедленное связывание через вложенный лямбда-вызов:
```
funcs = [(lambda i: (lambda x: i*x))(i) for i in range(5)]
```
Тоже вернёт 666 для `funcs[3](2)`.
Поведение в разных версиях Python
- Семантика позднего связывания (замыкания захватывают имя) одинакова в Python 2 и Python 3, поэтому проблема с одинаковыми значениями в созданных функциях остаётся.
- Отличие: в Python 2 переменная цикла list comprehension «протекала» внаружу (после `[...]` имя `i` оставалось доступным и равно 444); в Python 3 переменная цикла comprehension имеет локальную область и не просачивается в окружающий скоуп. Но это не решает саму проблему замыкания — лямбды всё равно увидят последнее значение цикла (444), если вы не зафиксируете значение при создании, как в вариантах выше.
26 Мая в 09:23
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир