我试图在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展开参数列表。
答案 0 :(得分:7)
你正在以疯狂的方式重新发明轮子。 izip
是它自己的反转:
>>> list(izip(*izip(range(10), range(10))))
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
但这并没有完全回答你的问题,是吗?
嵌套生成器的问题是一个范围问题,因为在最外层的生成器已经运行之前,最内层的生成器不会被使用:
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))
在这里,您生成三个生成器,每个生成器使用相同的变量,i
。没有制作此变量的副本。然后,tuple
耗尽最外层的发生器,创建一个生成元组:
>>> iunzip_v2((range(3), range(3)))
(<generator object <genexpr> at 0x1004d4a50>, <generator object <genexpr> at 0x1004d4aa0>, <generator object <genexpr> at 0x1004d4af0>)
此时,这些生成器中的每一个都将为elem[i]
的每个元素执行it
。由于i
现在对于所有三个生成器都等于3,因此每次都会获得最后一个元素。
第一个版本工作的原因是itemgetter(i)
是一个闭包,它有自己的作用域 - 所以每次它返回一个函数时,它会生成一个新的作用域,其中i
的值不会改变。
答案 1 :(得分:5)
好的,这有点棘手。当您使用类似i
的名称时,它所代表的值仅在运行时查找。在这段代码中:
return tuple((elem[i] for elem in it) for i, it in enumerate(iters))
您返回了许多生成器(elem[i] for elem in it)
,并且每个生成器都使用相同的名称i
。当函数返回时,tuple( .. for i in .. )
中的循环已结束,i
已设置为它的最终值(示例中为3
)。将这些生成器评估为列表后,它们都会创建相同的值,因为它们使用相同的i
。
顺便说一下:
unzip = lambda zipped: zip(*zipped)