有没有Pythonic的方法来闭合循环变量?
我刚看到Eric Lippert的文章《闭包中的循环变量被认为是有害的》,是在StackOverflow上找到的。经过一些实验,我发现这个问题在Python中也存在,而且解决起来甚至更麻烦。
>>> l = []
>>> for r in range(10):
... def foo():
... return r
... l.append(foo)
...
>>> for f in l:
... f()
...
9
9
9
# etc
而且,标准的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
我知道在Python中,这个问题并不是特别严重,因为Python通常不强调闭包对象结构。但我还是想知道,是否有明显的Python方式来处理这个问题,还是说我们得像在JavaScript中那样使用嵌套函数调用来创建真正的新变量呢?
>>> l = []
>>> for r in range(10):
... l.append((lambda x: lambda: x)(r))
...
>>> for f in l:
... f()
...
0
1
2
# etc
1 个回答
33
一种方法是使用带有默认值的参数:
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
之所以这样有效,是因为它在函数foo
的局部范围内定义了一个r
,并在定义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())
这样做的原因是,它在make_foo
的局部范围内定义了r
,并在调用make_foo(r)
时给它绑定了一个值。之后,当调用f()
时,会根据LEGB规则查找r
。虽然在foo
的局部范围内找不到r
,但在make_foo
的外部范围内可以找到。