itertools.groupby文档告诉我们
itertools.groupby(iterable, key=None)
[...]
的运作groupby()
类似于Unix中的uniq过滤器。每当键函数的值发生变化时,它都会生成一个中断或新组(这就是为什么通常需要使用相同的键函数对数据进行排序)。这种行为与 SQL 的 GROUP BY 不同,后者聚合公共元素,无论其输入顺序如何。
返回的组本身就是一个迭代器,它与以下对象共享底层可迭代对象groupby()
。因为源是共享的,所以当`groupby()对象前进时,前一个组不再可见。因此,如果稍后需要该数据,它应该存储为列表 [--]
So the 假设从最后一段可以看出,生成的列表将是空列表[]
,因为迭代器已经前进,并且满足StopIteration
;但在 CPython 中结果却令人惊讶[9]
.
这是因为_grouper迭代器落后于原始迭代器一项,这是因为groupby
需要向前查看一个项目以查看它是否属于当前组或下一个组,但它必须能够稍后生成该项目作为新组的第一个项目。
但是,那currkey
and currvalue
的属性groupby
are not重置时原始迭代器已耗尽, so currvalue
仍然指向迭代器的最后一项。
CPython 文档实际上包含以下等效代码,它也具有与 C 版本代码完全相同的行为:
class groupby:
# [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
def __init__(self, iterable, key=None):
if key is None:
key = lambda x: x
self.keyfunc = key
self.it = iter(iterable)
self.tgtkey = self.currkey = self.currvalue = object()
def __iter__(self):
return self
def __next__(self):
while self.currkey == self.tgtkey:
self.currvalue = next(self.it) # Exit on StopIteration
self.currkey = self.keyfunc(self.currvalue)
self.tgtkey = self.currkey
return (self.currkey, self._grouper(self.tgtkey))
def _grouper(self, tgtkey):
while self.currkey == tgtkey:
yield self.currvalue
try:
self.currvalue = next(self.it)
except StopIteration:
return
self.currkey = self.keyfunc(self.currvalue)
值得注意的是__next__
找到下一组的第一项,并将其密钥存储到self.currkey
及其价值self.currvalue
。但关键是线
self.currvalue = next(self.it) # Exit on StopIteration
When next
throws StopItertion
the self.currvalue
仍然包含前一组的最后一个键。现在,当y[1]
被制成一个list
, it first产生的值self.currvalue
,然后才运行next()
在底层迭代器上(并且满足StopIteration
again).
尽管文档中有 Python 等效项,但其行为与 CPython、IronPython、Jython 和 PyPy 中的权威 C 代码实现完全相同,给出了不同的结果。