Разберите поведение этого Python-фрагмента и объясните результат; for i in range(5): funcs = []; funcs.append(lambda: i); print([f() for f in funcs]) — что здесь не так и как исправить так, чтобы каждая функция возвращала своё i
Разбор поведения Исходный фрагмент: for i in range(5): funcs = [] funcs.append(lambda: i) print([f() for f in funcs]) Что происходит и почему: - В этом коде список `funcs` создаётся внутри цикла и сразу содержит одну функцию, которая захватывает имя `i`. - Эта функция вызывается немедленно в той же итерации, поэтому она возвращает текущее значение `i`. - Поэтому вывод будет: [0][0][0], [1][1][1], [2][2][2], [3][3][3], [4][4][4] — по одному элементу в каждой итерации. Типичная проблема замыканий (которая, вероятно, имелась в виду) проявляется, если вы хотите собрать все функции в список и вызвать их после цикла. Например: funcs = [] for i in range(5): funcs.append(lambda: i) print([f() for f in funcs]) В этом случае все функции захватывают одно и то же имя `i` (late binding), и при вызове после цикла все вернут последнее значение `i` — [4,4,4,4,4][4,4,4,4,4][4,4,4,4,4]. Как исправить, чтобы каждая функция «захватила» своё значение `i`: 1) Использовать параметр по умолчанию у lambda (самый простой): funcs = [] for i in range(5): funcs.append(lambda i=i: i) print([f() for f in funcs]) # -> [0, 1, 2, 3, 4] 2) Использовать фабрику функций: def make(i): return lambda: i funcs = [make(i) for i in range(5)] print([f() for f in funcs]) # -> [0, 1, 2, 3, 4] 3) Использовать functools.partial: from functools import partial funcs = [] for i in range(5): funcs.append(partial(lambda x: x, i)) print([f() for f in funcs]) # -> [0, 1, 2, 3, 4] Короткое объяснение механизма: lambda захватывает ссылку на переменную `i`, а не её текущее значение; использование `i=i` или явной фабрики создаёт новую локальную переменную со значением на момент создания, тем самым фиксируя его.
Исходный фрагмент:
for i in range(5):
funcs = []
funcs.append(lambda: i)
print([f() for f in funcs])
Что происходит и почему:
- В этом коде список `funcs` создаётся внутри цикла и сразу содержит одну функцию, которая захватывает имя `i`.
- Эта функция вызывается немедленно в той же итерации, поэтому она возвращает текущее значение `i`.
- Поэтому вывод будет: [0][0][0], [1][1][1], [2][2][2], [3][3][3], [4][4][4] — по одному элементу в каждой итерации.
Типичная проблема замыканий (которая, вероятно, имелась в виду) проявляется, если вы хотите собрать все функции в список и вызвать их после цикла. Например:
funcs = []
for i in range(5):
funcs.append(lambda: i)
print([f() for f in funcs])
В этом случае все функции захватывают одно и то же имя `i` (late binding), и при вызове после цикла все вернут последнее значение `i` — [4,4,4,4,4][4,4,4,4,4][4,4,4,4,4].
Как исправить, чтобы каждая функция «захватила» своё значение `i`:
1) Использовать параметр по умолчанию у lambda (самый простой):
funcs = []
for i in range(5):
funcs.append(lambda i=i: i)
print([f() for f in funcs]) # -> [0, 1, 2, 3, 4]
2) Использовать фабрику функций:
def make(i):
return lambda: i
funcs = [make(i) for i in range(5)]
print([f() for f in funcs]) # -> [0, 1, 2, 3, 4]
3) Использовать functools.partial:
from functools import partial
funcs = []
for i in range(5):
funcs.append(partial(lambda x: x, i))
print([f() for f in funcs]) # -> [0, 1, 2, 3, 4]
Короткое объяснение механизма: lambda захватывает ссылку на переменную `i`, а не её текущее значение; использование `i=i` или явной фабрики создаёт новую локальную переменную со значением на момент создания, тем самым фиксируя его.