You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python3.6中itertools.groupby的反直觉行为技术问询

为什么itertools.groupby返回的组迭代器是空的?

哈哈,这个坑我也踩过好几次!itertools.groupby的这个行为确实非常反直觉,尤其是当你先把它的结果转成list之后再处理的时候,问题出在两个核心点上:

1. groupby的组迭代器是“一次性”且依赖原迭代器状态的

itertools.groupby不会提前把所有组的元素都存下来,它是惰性遍历输入序列的。当你调用groupby时,它返回的是一个迭代器,每次生成一个组(键+组迭代器)。而每个组的迭代器并不是独立的——它和groupby的主迭代器共享同一个输入序列的位置。

也就是说:

  • 当你获取下一个组的时候,groupby会先把当前组的所有元素都遍历完(跳过),直到遇到新的键,才会生成新的组。
  • 如果你先把所有组都存到list里(比如groups = list(groupby(...))),那么在生成这个list的过程中,groupby已经把整个输入序列遍历了一遍,前面的组的迭代器已经没有元素可以取了——因为输入序列的指针已经走到了后面的位置。

2. 你的代码的问题所在

你把groupby的结果转成了list,这就导致在生成list的过程中:

  • 第一个组(键'5')的迭代器在groupby生成第二个组时,已经被耗尽了(因为groupby要跳过所有'5'的元素才能找到'10'的元素)。
  • 同理,第二个组(键'10')的迭代器在生成第三个组时被耗尽,第三个组(键'20')的迭代器在生成第四个组时被耗尽。
  • 只有最后一个组(键'100')的迭代器还能取到元素,因为后面没有新的键了,groupby没有跳过它的元素。

解决方法

有两种正确的做法:

方法一:直接遍历groupby的结果,不转成list

在获取每个组的时候直接处理元素,不要先把组存起来:

import itertools
a = ['1D.5', '2D.5', '3D.5', '1D.10', '2D.10', '3D.10', '1D.20', '2D.20', '3D.20', '1D.100', '2D.100', '3D.100']
for gname, glist in itertools.groupby(a, key=lambda x: x.split('.')[-1]):
    print(list(glist))

这样运行会得到正确的结果:

['1D.5', '2D.5', '3D.5']
['1D.10', '2D.10', '3D.10']
['1D.20', '2D.20', '3D.20']
['1D.100', '2D.100', '3D.100']

方法二:提前把每个组的元素存成list

如果你需要把所有组都保存下来之后再处理,可以在生成组的时候就把元素转成list:

import itertools
a = ['1D.5', '2D.5', '3D.5', '1D.10', '2D.10', '3D.10', '1D.20', '2D.20', '3D.20', '1D.100', '2D.100', '3D.100']
groups = [(gname, list(glist)) for gname, glist in itertools.groupby(a, key=lambda x: x.split('.')[-1])]
for gname, glist in groups:
    print(glist)

这样groups里的每个组都已经是保存好的元素列表,后续遍历不会有问题。

额外提醒:groupby要求输入序列按分组键排序

哦,对了,还有一个容易踩的坑:groupby只会把连续相同键的元素分到一组。如果你的输入序列没有按分组键排序,即使有相同键的元素,也会被分到不同的组里。比如如果你的列表是['1D.5', '1D.10', '2D.5'],按'.'后的部分分组的话,'1D.5'和'2D.5'会被分成两个组。不过在你的例子里,输入序列已经是按分组键排序好的,所以这个问题没出现,但还是要记住这个点!

内容的提问来源于stack exchange,提问作者Dilawar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:32:42