为什么Python的yield语句形成一个闭包?

2024-01-10

我有两个返回函数列表的函数。这些函数接受一些数字x并添加i to it. i是一个从 0 到 9 递增的整数。

def test_without_closure():
    return [lambda x: x+i for i in range(10)]



def test_with_yield():
    for i in range(10):
        yield lambda x: x+i

我希望test_without_closure返回包含 10 个函数的列表,每个函数添加9 to x since i的值为9.

print sum(t(1) for t in test_without_closure()) # prints 100

我期望test_with_yield也会有相同的行为,但它正确地创建了 10 个函数。

print sum(t(1) for t in test_with_yield()) # print 55

我的问题是,yielding 是否形成 Python 中的闭包?


在 Python 中,Yielding 不会创建闭包,而 lambda 会创建闭包。在“test_without_closure”中得到全 9 的原因并不是因为没有闭包。如果没有,您将无法访问i根本不。问题是所有闭包都包含对同一 i 变量的引用,该变量在函数末尾为 9。

这种情况与test_with_yield。那么,为什么会得到不同的结果呢?因为yield暂停函数的运行,因此可以在到达函数末尾之前(即之前)使用生成的 lambdai是 9。要了解这意味着什么,请考虑以下两个使用示例test_with_yield:

[f(0) for f in test_with_yield()]
# Result: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

[f(0) for f in list(test_with_yield())]
# Result: [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]

这里发生的情况是,第一个示例生成一个 lambda(当 i 为 0 时),调用它(i 仍然为 0),然后前进该函数直到生成另一个 lambda(i 现在为 1),调用该 lambda,依此类推。重要的是每个 lambda 在控制流返回之前被调用test_with_yield(即在 i 的值改变之前)。

在第二个示例中,我们首先创建一个列表。因此,第一个 lambda 被生成(i 为 0)并放入列表中,第二个 lambda 被创建(i 现在为 1)并放入列表中......直到最后一个 lambda 被生成(i 现在为 9)并放入进入列表。和then我们开始调用 lambda。所以自从i现在是 9,所有 lambda 都返回 9。


1 这里重要的一点是闭包保存对变量的引用,而不是创建闭包时它们所保存的值的副本。这样,如果您分配给 lambda 内部的变量(或内部函数,它以与 lambda 相同的方式创建闭包),这也会更改 lambda 外部的变量,如果您更改外部的值,则该更改将是在 lambda 内部可见。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么Python的yield语句形成一个闭包? 的相关文章

随机推荐