有没有Pythonic的方法来闭合循环变量?

29 投票
1 回答
2108 浏览
提问于 2025-04-17 10:43

我刚看到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的外部范围内可以找到。

撰写回答