Подтвердить что ты не робот

Есть ли способ 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
4b9b3361

Ответ 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.