【问题标题】:itertools groupby object not outputting correctlyitertools groupby 对象未正确输出
【发布时间】:2018-07-17 05:33:32
【问题描述】:

我试图使用 itertools.groupby 来帮助我按正或负属性对整数列表进行分组,例如:

输入

[1,2,3, -1,-2,-3, 1,2,3, -1,-2,-3] 

将返回

[[1,2,3],[-1,-2,-3],[1,2,3],[-1,-2,-3]]

但是如果我:

import itertools

nums = [1,2,3, -1,-2,-3, 1,2,3, -1,-2,-3]
group_list = list(itertools.groupby(nums, key=lambda x: x>=0))
print(group_list)
for k, v in group_list:
    print(list(v))
>>>
[]
[-3]
[]
[]

但如果我不list() groupby 对象,它会正常工作:

nums = [1,2,3, -1,-2,-3, 1,2,3, -1,-2,-3]
group_list = itertools.groupby(nums, key=lambda x: x>=0)
for k, v in group_list:
    print(list(v))
>>>
[1, 2, 3]
[-1, -2, -3]
[1, 2, 3]
[-1, -2, -3]

我不明白的是,groupby对象是由一对key和_grouper对象组成的迭代器,groupby对象的list()调用不应该消耗_grouper对象吗?

即使它确实消耗了,我是如何从第二个元素中获得[-3] 的?

【问题讨论】:

  • 只需使用列表理解:groups = [list(g) for _, g in groupby(nums, lambda n: n >= 0)].
  • @ChristianDean 嘿,又是你!我知道我应该如何做正确,但我不明白为什么调用list() 会出错。
  • @Code_Control_jxie0755 是的,我经常在现场巡逻 :-)。看完下面的答案,你是否还在困惑?如果是,通过什么方式?
  • @ChristianDean 在阅读了附加的段落后,现在我明白了!

标签: python iterator grouping itertools


【解决方案1】:

根据the docs,明确指出推进groupby 对象会使前一个组不可用(实际上是空的):

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

基本上,在推进groupby 对象之前,您需要一个将组迭代器转换为lists 的listcomp,而不是直接使用list 构造函数进行list-ifying,替换:

group_list = list(itertools.groupby(nums, key=lambda x: x>=0))

与:

group_list = [(k, list(g)) for k, g in itertools.groupby(nums, key=lambda x: x>=0)]

大多数itertools 模块类型的设计旨在避免隐式存储数据,因为它们旨在用于潜在的巨大输入。如果所有的 groupers 都存储了来自输入的所有数据的副本(并且 groupby 对象必须确保追溯填充它们),它会变得丑陋,并且可能会意外地破坏内存。根据 Python 之禅,通过强制您显式存储值,您不会意外地存储无限量的数据:

显式优于隐式。

【讨论】:

  • 我明白了,第二个代码 sn-p 是您建议替换的方式。但我仍然不太明白你从文档中引用的内容,也不明白 [-3] 来自哪里
  • @Code_Control_jxie0755:每次你从groupby 中拉出一个新的key/group 对(推进groupby 迭代器),任何现有的groups 都会被有效地清空。 groupby 超级懒惰;它只保留底层迭代器的一个副本并按需推进它,或者每次组迭代一次,或者跳过组的所有剩余成员(如果 groupby 对象本身是先进的)。没有单独的状态。
  • @Code_Control_jxie0755:[-3] 是一个实现怪癖;当您用完 groupby 时,它会处于处理负组的状态。您从中读取的第一个负组对象并不真正知道它是无效的,因此它会在缓存中提取最终值并说“嘿,这完全是我组的一部分”并产生它。它可能不应该,所以这是实现中的一个小错误(不要依赖它),但这并不是那么重要;在超越它之后使用组迭代器是 Python 最接近未定义行为的东西,因此行为怪异并不是真的出乎意料。
  • @Code_Control_jxie0755:酷。如果您想更清楚地演示-3 的来源,请将nums 更改为nums = [1,2,3,-1,-2,-3,4,5,6,-4,-5,-6],然后执行[(k, list(g)) for k, g in list(itertools.groupby(nums, key=lambda x: x>=0))]。您会注意到第二组现在没有产生-3,而是产生-6(第四组的最后一个成员)。就像我说的,它本质上是未定义的行为和实现的怪癖。
  • @Code_Control_jxie0755:是的。使用与-6 匹配的key 值进行高级的第一组恰好是拉取缓存在groupby 对象内的-6(当组结束时它需要缓存一个值,以便数据下一组不会丢失;它应该在迭代器耗尽时清除它,但看起来实现没有明确地这样做,它只是徘徊)。第二组查找所有负值,在缓存中找到负值,生成它并清除缓存,但没有意识到它的“真实”组早已过期。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-25
  • 1970-01-01
  • 1970-01-01
  • 2021-08-18
  • 2016-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多