Есть ли способ Pythonic закрыть переменную цикла?
Я просто столкнулся с Эриком Липпертом Закрытие переменной цикла, считающейся вредной через SO, и, после эксперимента, понял, что та же проблема существует ( и еще труднее обойти) в Python.
>>> l = []
>>> for r in range(10):
... def foo():
... return r
... l.append(foo)
...
>>> for f in l:
... f()
...
9
9
9
# etc
и стандартное обходное решение С# не работает (я предполагаю из-за природы ссылок в Python)
>>> l = []
>>> for r in range(10):
... r2 = r
... def foo():
... return r2
... l.append(foo)
...
>>> for f in l:
... f()
...
9
9
9
# etc
Я понимаю, что это не большая проблема в Python с его общим упором на структуры объектов, не связанных с закрытием, но мне любопытно, есть ли очевидный питоновский способ справиться с этим, или нам нужно идти JS маршрут вложенных вызовов функций для создания фактически новых vars?
>>> l = []
>>> for r in range(10):
... l.append((lambda x: lambda: x)(r))
...
>>> for f in l:
... f()
...
0
1
2
# etc
Ответы
Ответ 1
Один из способов - использовать параметр со значением по умолчанию:
l = []
for r in range(10):
def foo(r = r):
return r
l.append(foo)
for f in l:
print(f())
дает
0
1
2
3
4
5
6
7
8
9
Это работает, потому что он определяет локальную область r
in foo
и привязывает значение по умолчанию к ней в момент времени foo
.
Другой способ - использовать функцию factory:
l = []
for r in range(10):
def make_foo(r):
def foo():
return r
return foo
l.append(make_foo(r))
for f in l:
print(f())
Это работает, потому что он определяет r
в локальной области make_foo
и привязывает значение к нему при вызове make_foo(r)
. Позже, когда вызывается f()
, поиск r
выполняется с помощью правила LEGB. Хотя r
не найден в локальной области foo
, он находится в охватывающей области make_foo
.