def multipliers():
return [lambda x : i * x for i in range(4)]
print [m(2) for m in multipliers()]
Run Code Online (Sandbox Code Playgroud)
我部分理解(很危险)i
所有功能都相同的原因,因为Python的闭包是后期绑定。
输出是[6, 6, 6, 6]
(不像[0, 2, 4, 6]
我期望的那样)。
我看到它可以与生成器一起正常工作,我的预期输出来自以下版本。
def multipliers():
return (lambda x : i * x for i in range(4))
print [m(2) for m in multipliers()]
Run Code Online (Sandbox Code Playgroud)
任何简单的解释为什么它可以在以下版本中使用?
It only works because you call each function before the next one is created. The generator is lazy, it yields each function immediately, so before i
is incremented. Compare if you force all of the generator to be consumed before you call the functions:
>>> def multipliers():
... return (lambda x : i * x for i in range(4))
...
>>> print [m(2) for m in multipliers()]
[0, 2, 4, 6]
>>> print [m(2) for m in list(multipliers())]
[6, 6, 6, 6]
Run Code Online (Sandbox Code Playgroud)
If you want early binding then you can simulate it here with default arguments:
>>> def multipliers():
... return (lambda x, i=i : i * x for i in range(4))
...
>>> print [m(2) for m in multipliers()]
[0, 2, 4, 6]
>>> print [m(2) for m in list(multipliers())]
[0, 2, 4, 6]
Run Code Online (Sandbox Code Playgroud)
To clarify my comment about the generator being lazy: the generator (lambda x : i * x for i in range(4))
will go through values of i
from 0 to 3 inclusive, but it yields the first function while i
is still 0, at that point it hasn't bothered to do anything about the cases for 1 to 3 (which is why we say it is lazy).
The list comprehension [m(2) for m in multipliers()]
calls the first function m
immediately, so i
is still 0. Then the next iteration of the loop retrieves another function m
where i
is now 1. Again the function is called immediately so it sees i
as 1. And so on.
您正在寻找对复杂现象的简单解释,但我会尽量保持简短。
第一个函数返回一个函数列表,每个函数都是函数的闭包multipliers
。因此,解释器存储对“单元格”的引用,引用i
局部变量,允许该值在创建它的函数调用结束后继续存在,并且其局部命名空间已被销毁。
不幸的是,单元格中的引用是函数终止时变量的值,而不是它用于创建 lambda 时的值(因为它在循环中使用了四次,解释器必须创建每次使用都有一个单独的单元格,但它没有)。
您的第二个函数返回一个生成器表达式,它有自己的本地命名空间,i
在处理yield
ed 结果期间暂停时保留本地变量的值(在本例中,特别是)。
您会发现您可以将其显式地重铸为生成器函数,这可能有助于解释第二个示例的操作:
def multipliers():
for i in range(4):
yield lambda x : i * x
Run Code Online (Sandbox Code Playgroud)
这也给出了所需的结果。
归档时间: |
|
查看次数: |
242 次 |
最近记录: |