2012-01-20 37 views
18

我刚刚通过SO跑遍了Eric Lippert的Closing over the loop variable considered harmful,并且在试验后发现,在Python中存在相同的问题(甚至更难以避开)。是否有Pythonic方法来关闭循环变量?

>>> 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的问题它一般强调非关闭对象结构,但是我很好奇是否有明显的Pythonic方法来处理这个问题,或者我们必须去嵌套函数调用的JS路由来创建实际上新的变量?

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

回答

23

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

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 rule查找r。虽然在本地范围foo中找不到r,但在make_foo的封闭范围内可以找到它。

+1

哦,我喜欢这个默认的参数技巧。该函数工厂在语义上类似于我的末尾的双lambda事物。不过,Lambda工厂是可读性的祸根。 – quodlibetor

相关问题