itertools.groupby 功能似乎不一致

itertools.groupby function seems inconsistent

我无法准确理解此函数的作用,因为我猜是围绕其使用的编程魔法?

我喜欢它 returns 键列表(字符串中的唯一字母)与迭代器配对,引用原始字符串中每个字母的编号列表,但有时好像不是这样。

例如:

import itertools

x = list(itertools.groupby("AAABBB"))
print x

打印:

[('A', <itertools._grouper object at 0x101a0b050), 
 ('B', <itertools._grouper object at 0x101a0b090)]

这似乎是正确的,我们将我们的唯一键与迭代器配对。但是当我 运行:

print list(x[0][1])

我得到:

[]

当我运行

for k, g in x:
    print k + ' - ' + g

我得到:

B - <itertools._grouper object at 0x1007eedd5>

它忽略第一个元素。这似乎违反直觉,因为如果我稍微改变一下语法:

[list(g) for k, g in itertools.groupby("AAABBB")]

我得到:

[["A", "A", "A"], ["B", "B", "B"]]

这是正确的,并且符合我认为该功能应该做的事情。

但是,如果我再次稍微更改一下语法:

[list(thing) for thing in [g for k, g in itertools.groupby(string)]]

我回来了:

[[], ['B']]

这两个列表理解应该是直接等价的,但是它们 return 不同的结果。

这是怎么回事?非常感谢您的见解。

要获得您期望的答案,请将返回的迭代器转换为列表。

Groupby 延迟使用输入迭代器(这意味着它只在需要时读取数据)。要找到一个新组,它需要读取下一个不相等的元素(下一个组的第一个成员)。如果您 list 子组迭代器,它将把输入推进到当前组的末尾。

一般来说,如果你前进到下一个组,那么之前返回的子组迭代器将没有数据并且会显示为空。所以,如果你需要子组迭代器中的数据,你需要列出然后前进到下一个组。

出现这种行为的原因是迭代器一次只能查找一个数据,不会在内存中保留任何不必要的内容。

下面是一些使所有操作可见的代码:

from itertools import groupby

def supply():
    'Make the lazy input visible'
    for c in 'aaaaabbbcdddddddeeee':
        print('supplying %r' % c)
        yield c

print("\nCase where we don't consume the sub-iterator")
for k, g in groupby(supply()):
    print('Got group for %r' % k)

print("\nCase where we do consume the sub-iterator before advancing")
for k, g in groupby(supply()):
    print('Got group for %r' % k)
    print(list(g))

在示例 "that is driving you crazy" 中,list 操作应用得太迟(在外部列表理解中)。解决办法是把list这一步移到内推:

>>> import itertools
>>> [list(g) for k, g in itertools.groupby('aaaaabbbb')]
>>> [['a', 'a', 'a', 'a', 'a'], ['b', 'b', 'b', 'b']]

如果您真的不关心节省内存,那么 运行 grouped = [list(g) for k, g in itertools.groupby(data)] 是一个非常合理的方法。然后,您可以随时在任何子列表中查找数据,而不受有关何时使用迭代器的规则的约束。一般来说,列表的列表比迭代器更容易使用。希望这有帮助:-)

文档已经解释了为什么你的 listcomps 不等价:

The returned group is itself an iterator that shares the underlying iterable with groupby(). Because the source is shared, when the groupby() object is advanced, the previous group is no longer visible. So, if that data is needed later, it should be stored as a list

你的

[list(g) for k, g in itertools.groupby("AAABBB")]

groupby()前进之前使用每个组,所以它有效。

你的

[list(thing) for thing in [g for k, g in itertools.groupby(string)]]

在生成所有组之前不使用任何组。完全不一样,并且出于引用文档解释的原因。