Ale*_*lex 24 python closures functional-programming yield
我有两个函数返回一个函数列表.函数接受一个数字x并添加i到它.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
Run Code Online (Sandbox Code Playgroud)
我希望test_without_closure返回一个包含10个函数的列表,每个函数都添加9到x自i的值以来9.
print sum(t(1) for t in test_without_closure()) # prints 100
Run Code Online (Sandbox Code Playgroud)
我希望它test_with_yield也会有相同的行为,但它正确地创建了10个函数.
print sum(t(1) for t in test_with_yield()) # print 55
Run Code Online (Sandbox Code Playgroud)
我的问题是,在Python中屈服形成一个闭包吗?
sep*_*p2k 29
Yielding不会在Python中创建闭包,lambdas会创建一个闭包.你在"test_without_closure"中得到所有9个的原因并不是没有关闭.如果没有,你将根本无法访问i.问题是所有闭包都包含对同一i变量的引用¹,该函数在函数末尾为9.
这种情况并没有太大的不同test_with_yield.那么,为什么你会得到不同的结果?因为yield暂停函数的运行,所以可以在函数结束之前使用生成的lambdas,即在i9 之前.要查看这意味着什么,请考虑以下两个使用示例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]
Run Code Online (Sandbox Code Playgroud)
这里发生的是第一个例子产生一个lambda(而我是0),调用它(我仍然是0),然后推进该函数,直到产生另一个lambda(我现在是1),调用lambda,依此类推.重要的是在控制流返回test_with_yield之前(即在i的值改变之前)调用每个lambda .
在第二个示例中,我们首先创建一个列表.所以第一个lambda被生成(i为0)并放入列表中,第二个lambda被创建(我现在是1)并被放入列表中......直到最后一个lambda被生成(我现在是9)并且放入进入清单.而随后我们开始调用lambda表达式.所以从i现在开始9岁,所有的lambda都返回9.
¹这里重要的一点是闭包持有对变量的引用,而不是创建闭包时它们所持有的值的副本.这样,如果你在lambda(或内部函数中)中分配变量,它会以与lambda相同的方式创建闭包,这也会改变lambda之外的变量,如果你改变外面的值,那么这个变化将是在lambda里面可见.
不,屈服与封闭无关.
以下是如何识别Python中的闭包:闭包是
一个功能
其中执行非限定名称查找
函数本身不存在名称绑定
但是名称的绑定存在于函数的局部作用域中,该函数的定义包含查找名称的函数的定义.
你观察到的行为差异的原因是懒惰,而不是与闭包有关.比较和对比以下内容
def lazy():
return ( lambda x: x+i for i in range(10) )
def immediate():
return [ lambda x: x+i for i in range(10) ]
def also_lazy():
for i in range(10):
yield lambda x:x+i
not_lazy_any_more = list(also_lazy())
print( [ f(10) for f in lazy() ] ) # 10 -> 19
print( [ f(10) for f in immediate() ] ) # all 19
print( [ f(10) for f in also_lazy() ] ) # 10 -> 19
print( [ f(10) for f in not_lazy_any_more ] ) # all 19
Run Code Online (Sandbox Code Playgroud)
请注意,第一个和第三个示例给出了相同的结果,第二个和第四个示例也是如此.第一个和第三个是懒惰的,第二个和第四个不是.
请注意,所有四个示例都提供了一堆关于最近绑定的闭包i,只是在第一个第三个案例中,您在重新绑定i之前评估闭包(甚至在您创建序列中的下一个闭包之前),而在第二种情况和第四种情况,你先等到i反弹到9(在你创建并收集了你将要制作的所有闭包之后),然后再评估闭包.
| 归档时间: |
|
| 查看次数: |
1867 次 |
| 最近记录: |