itertools.groupby:按对逐组迭代
我该如何成对地遍历groupby
的结果呢?我尝试的方法似乎不太奏效:
from itertools import groupby,izip
groups = groupby([(1,2,3),(1,2),(1,2),(3,4,5),(3,4)],key=len)
def grouped(iterable, n):
return izip(*[iterable]*n)
for g, gg in grouped(groups,2):
print list(g[1]), list(gg[1])
我得到的输出是:
[] [(1, 2), (1, 2)]
[] [(3, 4)]
我想要的输出是:
[(1, 2, 3)] [(1, 2), (1, 2)]
[(3, 4, 5)] [(3, 4)]
2 个回答
2
当你尝试查看groupby
的第二个键时,其实是在强迫它深入到源迭代器中去获取数据。因为通常没有地方可以存储第一个组的项目,所以这些项目就被丢弃了。
所以现在我们明白了,在查看第二组的键(或者项目)之前,我们需要确保已经存储了第一组的项目。
有些人可能会对此感到不满,但
>>> groups = groupby([(1, 2, 3), (1, 2), (1, 2), (3, 4, 5), (3, 4)], key=len)
>>> for i, j in ((list(i[1]), list(next(groups)[1])) for i in groups):
... print i, j
...
[(1, 2, 3)] [(1, 2), (1, 2)]
[(3, 4, 5)] [(3, 4)]
3
import itertools as IT
groups = IT.groupby([(1,2,3),(1,2),(1,2),(3,4,5),(3,4)], key=len)
groups = (list(group) for key, group in groups)
def grouped(iterable, n):
return IT.izip(*[iterable]*n)
for p1, p2 in grouped(groups, 2):
print p1, p2
[(1,2,3),(1,2),(1,2),(3,4,5),(3,4)]
产生
[(1, 2, 3)] [(1, 2), (1, 2)]
[(3, 4, 5)] [(3, 4)]
你发的代码很有意思。它有一个普通的问题和一个微妙的问题。
普通的问题是,itertools.groupby 返回的是一个迭代器,每次迭代时都会输出一个键和一个组。因为你只对组感兴趣,而不关心键,所以你需要类似这样的东西:
groups = (group for key, group in groups)
微妙的问题就比较难解释了——我也不太确定自己完全理解。我的猜测是:groupby 返回的迭代器把输入变成了一个迭代器。这个 groupby 迭代器就像是把底层数据迭代器包裹起来,类似于 csv.reader
是如何包裹一个底层文件对象迭代器的。你只能通过这个迭代器走一遍,只有这一遍。在配对 groups
中的项目时,itertools.izip 函数会让 groups
迭代器从第一个项目推进到第二个。因为你只能走一遍迭代器,所以第一个项目已经被消耗掉了,因此当你调用 list(g[1])
时,它是空的。
一个不太令人满意的解决办法是把 groups
中的迭代器转换成列表:
groups = (list(group) for key, group in groups)
这样 itertools.izip
就不会提前消耗它们。编辑:再想想,这个解决办法也不是那么糟糕。groups
仍然是一个迭代器,只有在被消耗时才会把 group
转换成列表。