是否有Pythonic方法来关闭循环变量?

quo*_*tor 20 python closures for-loop

我刚刚遇到了Eric Lippert关于通过SO 认为有害的循环变量Closing,并且在经过实验之后,意识到在Python中存在同样的问题(并且更难以绕过).

>>> l = []
>>> for r in range(10):
...   def foo():
...      return r
...   l.append(foo)
...
>>> for f in l:
...   f()
...
9
9
9
# etc
Run Code Online (Sandbox Code Playgroud)

并且,标准的C#解决方法不起作用(我假设因为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
Run Code Online (Sandbox Code Playgroud)

我认识到这在Python中并不是一个问题,它一般强调非闭包对象结构,但我很好奇是否有一种明显的Pythonic方法来处理这个问题,或者我们是否必须采用JS的路径嵌套函数调用实际创建新变量?

>>> l = []
>>> for r in range(10):
...     l.append((lambda x: lambda: x)(r))
...
>>> for f in l:
...     f()
...
0
1
2
# etc
Run Code Online (Sandbox Code Playgroud)

unu*_*tbu 24

一种方法是使用具有默认值的参数:

l = []
for r in range(10):
    def foo(r = r):
        return r
    l.append(foo)

for f in l:
    print(f())
Run Code Online (Sandbox Code Playgroud)

产量

0
1
2
3
4
5
6
7
8
9
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为它定义了rin foo的局部范围,并在foo定义时将默认值绑定到它.


另一种方法是使用函数工厂:

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())
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为它定义rmake_foo本地范围,并在make_foo(r)调用时将值绑定到它.稍后,在f()调用时, r使用LEGB规则查找.虽然r没有在当地范围内foo找到,但是在封闭范围内可以找到make_foo.