Python 嵌套生成器
我试图在Python 2.7.1上实现itertools.izip的反向功能。问题是我发现了一个问题,但我没有解释。 解决方案 1,iunzip_v1 完美运行。但解决方案 2. iunzip_v2 无法按预期工作。到目前为止,我还没有找到任何关于这个问题的相关信息,并且阅读了有关生成器的 PEP,听起来它应该可以工作,但事实并非如此。
import itertools
from operator import itemgetter
def iunzip_v1(iterable):
_tmp, iterable = itertools.tee(iterable, 2)
iters = itertools.tee(iterable, len(_tmp.next()))
return tuple(itertools.imap(itemgetter(i), it) for i, it in enumerate(iters))
def iunzip_v2(iterable):
_tmp, iterable = itertools.tee(iterable, 2)
iters = itertools.tee(iterable, len(_tmp.next()))
return tuple((elem[i] for elem in it) for i, it in enumerate(iters))
结果:
In [17]: l
Out[17]: [(0, 0, 0), (1, 2, 3), (2, 4, 6), (3, 6, 9), (4, 8, 12)]
In [18]: map(list, iunzip.iunzip_v1(l))
Out[18]: [[0, 1, 2, 3, 4], [0, 2, 4, 6, 8], [0, 3, 6, 9, 12]]
In [19]: map(list, iunzip.iunzip_v2(l))
Out[19]: [[0, 3, 6, 9, 12], [0, 3, 6, 9, 12], [0, 3, 6, 9, 12]]
似乎 iunzip_v2 使用的是最后一个值,因此生成器在第一个生成器内创建时不会保留该值。 我错过了一些东西,但我不知道是什么。
如果有什么可以澄清我的情况,请提前致谢。
更新: 我在这里找到了解释 PEP-289 ,我第一次阅读是在 PEP-255。 我试图实现的解决方案是一种懒惰的解决方案,因此:
zip(*iter) or izip(*...)
对我不起作用,因为 *arg 扩展了参数列表。
I was trying to implement the reverse function of itertools.izip on Python 2.7.1. The thing is that I find a problem, and I don't have an explantion.
Solution 1, iunzip_v1 works perfectly. But solution 2. iunzip_v2, doesn't works as expected. Til now, I haven't found any relevant information about this problem, and reading the PEP about generators, it sound it should work, but it doesn't.
import itertools
from operator import itemgetter
def iunzip_v1(iterable):
_tmp, iterable = itertools.tee(iterable, 2)
iters = itertools.tee(iterable, len(_tmp.next()))
return tuple(itertools.imap(itemgetter(i), it) for i, it in enumerate(iters))
def iunzip_v2(iterable):
_tmp, iterable = itertools.tee(iterable, 2)
iters = itertools.tee(iterable, len(_tmp.next()))
return tuple((elem[i] for elem in it) for i, it in enumerate(iters))
result:
In [17]: l
Out[17]: [(0, 0, 0), (1, 2, 3), (2, 4, 6), (3, 6, 9), (4, 8, 12)]
In [18]: map(list, iunzip.iunzip_v1(l))
Out[18]: [[0, 1, 2, 3, 4], [0, 2, 4, 6, 8], [0, 3, 6, 9, 12]]
In [19]: map(list, iunzip.iunzip_v2(l))
Out[19]: [[0, 3, 6, 9, 12], [0, 3, 6, 9, 12], [0, 3, 6, 9, 12]]
Seems that iunzip_v2 is using the last value, so the generators aren't keeping the value while they are created inside the first generator.
I'm missing something and I don't know what is.
Thanks in advance if something can clarify me this situation.
UPDATE:
I've found the explanation here PEP-289, my first read was at PEP-255.
The solution I'm trying to implement is a lazy one, so:
zip(*iter) or izip(*...)
doesn't work for me, because *arg expand the argument list.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
你正在以一种疯狂的方式重新发明轮子。
izip
是它自己的逆:但这并不能完全回答你的问题,不是吗?
嵌套生成器的问题是范围界定问题,这是因为最里面的生成器在最外面的生成器运行之前不会被使用:
在这里,您生成三个生成器,每个生成器使用相同的变量 ,
我
。不会创建此变量的副本。然后,tuple 耗尽最外层的生成器,创建一个生成器元组:此时,每个生成器都将为它的每个元素执行 elem[i] 。由于所有三个生成器的 i 现在都等于 3,因此每次都会获得最后一个元素。
第一个版本起作用的原因是
itemgetter(i)
是一个闭包,有自己的作用域——所以每次它返回一个函数时,它都会生成一个新的作用域,其中的值>i
不会改变。You're reinventing the wheel in a crazy way.
izip
is its own inverse:But that doesn't quite answer your question, does it?
The problem with your nested generators is a scoping problem that happens because the innermost generators don't get used until the outermost generator has already run:
Here, you generate three generators, each of which uses the same variable,
i
. Copies of this variable are not made. Then,tuple
exhausts the outermost generator, creating a tuple of generators:At this point, each of these generators will execute
elem[i]
for each element ofit
. And sincei
is now equal to 3 for all three generators, you get the last element each time.The reason the first version works is that
itemgetter(i)
is a closure, with its own scope -- so every time it returns a function, it generates a new scope, within which the value ofi
does not change.好吧,这有点棘手。当您使用像
i
这样的名称时,它所代表的值将在运行时查找。在此代码中:您返回多个生成器
(其中 elem[i] 表示 elem)
,并且每个生成器都使用相同的名称i
。当函数返回时,tuple( .. for i in .. )
中的循环已结束,i
已设置为其最终值 (3
代码> 在你的例子中)。一旦您将这些生成器评估为列表,它们都会创建相同的值,因为它们使用相同的i
。顺便提一句:
Ok this is a bit tricky. When you use a name like
i
the value it stands for is looked up just during runtime. In this code:you return a number of generators,
(elem[i] for elem in it)
and each of them uses the same namei
. When the function returns, the loop intuple( .. for i in .. )
has ended andi
has been set to it's final value (3
in your example). Once you evaluate these generators to lists, they all create the same values because they are using the samei
.Btw: