【问题标题】:Why does `zip` seem to consume a `groupby` iterable?为什么`zip`似乎消耗了`groupby`迭代?
【发布时间】:2016-12-27 20:02:49
【问题描述】:

所以使用itertools.groupby() 拆分列表相当简单。

>>> import itertools as it
>>> iterable = it.groupby([1, 2, 3, 4, 5, 2, 3, 4, 2], lambda p: p==2)
>>> for x, y in iterable:
...     print(x, list(y))
...     next(iterable)
False [1]
False [3, 4, 5]
False [3, 4]

按预期工作。但是使用zip这种常见的python习语多次向上迭代迭代器以一次遍历2个似乎会破坏事情。

>>> iterable = it.groupby([1, 2, 3, 4, 5, 2, 3, 4, 2], lambda p: p==2)
>>> for (x, y), _ in zip(iterable, iterable):
...     print(x, list(y))
False []
False []
False []

添加print(y) 显示了预期的嵌套可迭代<itertools._grouper object at 0xXXXXXXXX>,但我显然遗漏了为什么grouper 对象为空的原因。有人能解释一下吗?

如果我有一个不均匀的列表并使用itertools.zip_longest,我会得到一个更奇怪的结果:

>>> iterable = it.groupby([1, 2, 3, 4, 5, 2, 3, 4], lambda p: p==2)
>>> for (x, y), _ in it.zip_longest(iterable, iterable, fillvalue=None):
...     print(x, list(y))
False []
False []
False [4]

更新:简单的解决方法是使用itertools.islice()

>>> iterable = it.groupby([1, 2, 3, 4, 5, 2, 3, 4, 2], lambda p: p==2)
>>> for x, y in it.islice(iterable, None, None, 2):
...     print(x, list(y))
False [1]
False [3, 4, 5]
False [3, 4]

【问题讨论】:

标签: python python-3.x itertools


【解决方案1】:

groupby 文档警告您

返回的组本身是一个迭代器,它与 groupby() 共享底层迭代。因为源是共享的,所以当 groupby() 对象前进时,上一个组不再可见

当您的zip 生成((key, group), (key, group)) 对时,它会将groupby 迭代器超过第一组,从而使第一组无法使用。您需要在前进之前实现组:

iterable = ((key, list(group)) for (key, group) in it.groupby([1, 2, 3, 4, 5, 2, 3, 4, 2], lambda p: p==2))
for (x, y), _ in zip(iterable, iterable):
    print(x, y)

【讨论】:

  • 谢谢,有道理。 zip_longest 最后结果的任何原因只是 [4] 而不是 [3, 4],因为 grouper 在最终迭代中没有被推进。
  • @AChampion:但它先进的,因为groupby 需要推进底层迭代器以找出没有最终组。 (之所以使用 [4] 而不是 [] 是因为提供的链接库中提到的实现细节。)
【解决方案2】:

因为一旦您到达itertools.groupby 中的下一项,它就会丢弃之前遇到的任何_grouper-generators。

他们将看到的最新项目:

>>> iterable = it.groupby([1, 2, 3, 4, 5, 2, 3, 4, 2], lambda p: p==2)
>>> for (x, y), (x2, y2) in zip(iterable, iterable):
...     print(x2, list(y2))
True [2]
True [2]
True [2]

documentation 包含有关此行​​为的警告:

返回的组本身就是一个迭代器,它与 groupby() 共享底层迭代。因为源是共享的,所以当 groupby() 对象高级时,之前的组不再可见。因此,如果以后需要该数据,则应将其存储为列表

因此,通过使用 (x, y), _ in zip(iterable, iterable),您实际上将迭代器提高了 2(即使最新结果转储到 _),而第一个(您的 x, y)不再可用!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-14
    • 1970-01-01
    • 2020-05-30
    • 2013-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多