为什么Python yield语句构成一个闭包? [英] Why do Python yield statements form a closure?

查看:93
本文介绍了为什么Python yield语句构成一个闭包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个函数,它们返回一个函数列表.这些函数接受数字x并添加i. i是从0到9的整数.

I have two functions that return a list of functions. The functions take in a number x and add i to it. i is an integer increasing from 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添加到x,因为i的值是9.

I would expect test_without_closure to return a list of 10 functions that each add 9 to x since i's value is 9.

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

我希望test_with_yield也会具有相同的行为,但是它可以正确创建10个函数.

I expected that test_with_yield would also have the same behavior, but it correctly creates the 10 functions.

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

我的问题是,是否在Python中产生了闭包?

My question is, does yielding form a closure in Python?

推荐答案

Yielding不会在Python中创建闭包,lambdas会创建闭包.您在"test_without_closure"中获得所有9的原因不是没有关闭.如果没有,您将完全无法访问i.问题在于所有闭包都包含对同一个i变量的引用¹,该变量在函数末尾将为9.

Yielding does not create a closure in Python, lambdas create a closure. The reason that you get all 9s in "test_without_closure" isn't that there's no closure. If there weren't, you wouldn't be able to access i at all. The problem is that all closures contain a reference¹ to the same i variable, which will be 9 at the end of the function.

这种情况在test_with_yield中并没有太大不同.那么,为什么会得到不同的结果呢?由于yield会中止函数的运行,因此可以在到达函数末尾之前(即,在i为9之前)使用产生的lambda.要了解这意味着什么,请考虑以下两个使用<的示例c8>:

This situation isn't much different in test_with_yield. Why, then, do you get different results? Because yield suspends the run of the function, so it's possible to use the yielded lambdas before the end of the function is reached, i.e. before i is 9. To see what this means, consider the following two examples of using 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(使我现在为1),称为lambda,等等.重要的是在控制流返回到test_with_yield之前(即,在i的值更改之前)将调用每个lambda.

What's happening here is that the first example yields a lambda (while i is 0), calls it (i is still 0), then advances the function until another lambda is yielded (i is now 1), calls the lambda, and so on. The important thing is that each lambda is called before the control flow returns to test_with_yield (i.e. before the value of i changes).

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

In the second example, we first create a list. So the first lambda is yielded (i is 0) and put into the list, the second lambda is created (i is now 1) and put into the list ... until the last lambda is yielded (i is now 9) and put into the list. And then we start calling the lambdas. So since i is now 9, all lambdas return 9.

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

¹ The important bit here is that closures hold references to variables, not copies of the value they held when the closure was created. This way, if you assign to the variable inside a lambda (or inner function, which create closures the same way that lambdas do), this will also change the variable outside of the lambda and if you change the value outside, that change will be visible inside the lambda.

这篇关于为什么Python yield语句构成一个闭包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆