我最近不得不调试一些像这样的代码:
for key, group in itertools.groupby(csvGrid, lambda x: x[0]):
value1 = sum(row[1] for row in group)
value2 = sum(row[2] for row in group)
results.append([key, value1, value2])
在每个结果集中,value2
都显示为0
。当我查看它时,我发现代码第一次迭代group
时,它消耗了它,所以第二次有零元素迭代。
直观地说,我希望group
是一个可以无限次迭代的列表,但它的行为就像一个只能迭代一次的迭代器。有这么好的理由吗?
答案 0 :(得分:4)
itertools
是一个迭代器库,就像库中的其他所有内容一样,itertools.groupby
组是迭代器。所有itertools
中都没有一个函数可以返回序列。
groupby组是迭代器的原因与itertools中的其他所有东西都是迭代器的原因相同:
此外,这些组是迭代器,因为您可能只需要键,在这种情况下实现组将是一种浪费。
itertools.groupby
无意与任何LINQ构造,SQL子句或其他名称为“group by”的东西完全匹配。它的分组行为更接近于Unix的uniq
命令的扩展,而不是LINQ或SQL的扩展,尽管它使组成为一个意味着它不是uniq
的完全匹配。
作为你可以用itertools.groupby
做的事情的例子,你不能用我命名的其他工具,这里是一个游程编码器:
def runlengthencode(iterable):
for key, group in groupby(iterable):
yield (key, sum(1 for val in group))
答案 1 :(得分:2)
直观地说,我希望group是一个可以无限次迭代的列表,但它的行为就像一个只能迭代一次的迭代器。
那是对的。
有这么好的理由吗?
它可能更具内存效率:您不需要先构建整个列表,然后将其存储在内存中,然后再迭代它。相反,您可以在迭代时处理元素。
它可能提高CPU效率:通过不预先生成所有数据,例如:通过生成一个列表,你可以提前挽救:如果你找到一个匹配某个谓词的特定组,你可以停止迭代 - 不需要做进一步的工作。
是否需要所有数据并多次迭代它的决定不是被调用者硬编码的,而是留给调用者。
答案 2 :(得分:1)
来自docs
返回的组本身就是一个迭代器,它与groupby()共享底层的iterable。由于源是共享的,因此当groupby()对象处于高级时,前一个组将不再可见。因此,如果以后需要该数据,则应将其存储为列表
有趣的是,如果您不自己消耗g
,groupby
将在返回下一次迭代之前执行此操作。
>>> def vals():
... for i in range(10):
... print(i)
... yield i
...
>>> for k,g in itertools.groupby(vals(), lambda x: x<5):
... print('processing group')
...
0
processing group
1
2
3
4
5
processing group
6
7
8
9
答案 3 :(得分:0)
当尝试多次访问“ groupby”返回的迭代器时,我遇到了相同的问题。 基于Python3 doc,建议将迭代器传输到list,以便以后可以访问。