itertools.groupby要求数据按分组键预先排序,否则相邻相同键才分组;需先sorted再groupby,键函数要一致,注意None处理、迭代器只遍历一次、避免复杂计算。

groupby 要求数据已按分组键排序,否则会切碎同一组
这是最常踩的坑:itertools.groupby 不是“按值聚类”,而是“按相邻相同键切片”。如果嵌套列表里 key 相同的元素不连续,groupby 会把它们拆成多个组。
比如你有一组字典:[{'type': 'A'}, {'type': 'B'}, {'type': 'A'}],直接用 groupby(item['type']) 会得到三组(A、B、A),而不是两组(A×2、B×1)。
- 必须先用
sorted(data, key=lambda x: x['type'])排序,再传给groupby - 排序键要和
groupby的键函数完全一致,比如都用lambda x: (x['category'], x['status']) - 对大列表排序有开销,若原始数据已按某字段天然有序(如数据库查询结果按
created_at排),别盲目加sorted
嵌套结构中提取分组键时,注意 None 或缺失字段引发的 TypeError
当你对 [{'user': {'id': 1}}, {'user': None}] 这类不一致结构调用 lambda x: x['user']['id'],第二项会抛出 TypeError: 'NoneType' object is not subscriptable。
安全写法是用 get() 或条件表达式兜底:
key_func = lambda x: x.get('user', {}).get('id', 0)
# 或更明确:
key_func = lambda x: x['user']['id'] if x.get('user') and isinstance(x['user'], dict) else -1
- 避免在
key函数里做复杂计算或 I/O,它会被反复调用 - 若键依赖多层嵌套且可能为空,提前用
filter()清洗掉脏数据比在key里硬处理更清晰 - 字符串键要注意大小写和空格,必要时统一用
.strip().lower()
groupby 返回的是 (key, group_iterator),不是列表,不能多次遍历
groupby 的第二个返回值是个迭代器,读完即失效。常见错误是:打印一次、再想转 list 就空了。
正确做法是立刻转成你需要的结构:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
from itertools import groupby
<p>data = [{'tag': 'x'}, {'tag': 'x'}, {'tag': 'y'}]
sorted_data = sorted(data, key=lambda x: x['tag'])
groups = {k: list(g) for k, g in groupby(sorted_data, key=lambda x: x['tag'])}</p><h1>→ {'x': [{'tag': 'x'}, {'tag': 'x'}], 'y': [{'tag': 'y'}]}</h1><p></p>
- 不要对
g做多次list(g)或for循环,它只消费一次 - 如果只需要每组的统计量(如数量、最大值),用
sum(1 for _ in g)或max(..., default=...),避免全加载到内存 - 嵌套太深时,考虑用
collections.defaultdict(list)手动累积,逻辑更直白、调试更方便
替代方案:pandas.groupby 更适合真正复杂的嵌套/混合类型分组
当你的“嵌套列表”实际是带多种字段的记录集合(比如 JSON 行、API 响应),且需要聚合、过滤、跨字段关联时,itertools.groupby 很快会力不从心。
例如对 [{'id': 1, 'scores': [85, 92]}, {'id': 2, 'scores': [78, 88]}] 按平均分段(itertools 得自己算均值、映射区间、再排序分组;而 pandas 一行搞定:
import pandas as pd
df = pd.DataFrame(data)
df['avg'] = df['scores'].apply(lambda x: sum(x)/len(x) if x else 0)
df['grade'] = pd.cut(df['avg'], bins=[0,60,80,90,100], labels=['F','D','B','A'])
result = df.groupby('grade')['id'].list() # 或 .count(), .agg(...)
-
pandas自动处理缺失、类型转换、多重索引,itertools全得手写 - 小数据(itertools 更轻量;中大型或需分析操作,别硬扛
- 若项目已引入
pandas,优先用它——不是“重”,而是“少写 bug”
分组逻辑越靠近业务含义(比如“同一用户的所有订单”),越容易忽略排序前提;越靠近数据形态(比如“按 status 字段归类”),越容易栽在键提取的健壮性上。这两个点,比语法本身更值得花时间验证。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










