02
列表 / 字典操作 · 6 / 6
2.6 collections:Counter / defaultdict
💡 统计 token 频率、按 model 分组请求——你不会想手写循环的
2.6 collections:Counter / defaultdict
Counter:自动计数
from collections import Counter
c = Counter(["a", "b", "a", "c", "a"])
c["a"] # 3
c.most_common(2) # [("a", 3), ("b", 1)]
defaultdict:访问不存在的 key 时自动创建
from collections import defaultdict
d = defaultdict(list)
d["qwen"].append(1) # 不需要先判断 "qwen" 在不在
d["qwen"].append(2)
print(dict(d)) # {"qwen": [1, 2]}
对比 dict
d["qwen"] 在普通 dict 上会抛 KeyError,必须先 if "qwen" in d。defaultdict 直接省掉这步。
示范
from collections import Counter, defaultdict
# 1. 统计每个 model 的请求数
reqs = [
{"model": "qwen2.5-7b"},
{"model": "qwen2.5-7b"},
{"model": "llama3-8b"},
]
counts = Counter(r["model"] for r in reqs)
print(counts) # Counter({'qwen2.5-7b': 2, 'llama3-8b': 1})
print(counts.most_common(1)) # [('qwen2.5-7b', 2)]
# 2. 按 model 分组
by_model = defaultdict(list)
for r in reqs:
by_model[r["model"]].append(r)
print(dict(by_model))
✍️ 练习
输入:
log_lines = [
"INFO qwen2.5-7b ok",
"INFO llama3-8b ok",
"WARN qwen2.5-7b slow",
"INFO qwen2.5-7b ok",
"ERROR llama3-8b oom",
]
要求:
- 用
Counter数每个 level(INFO / WARN / ERROR)出现几次 - 用
defaultdict(list)把每个 model 对应的行存成 list
把两个结果都 print 出来。
💡 思路提示
点开看提示
split()默认按空白分,"INFO qwen2.5-7b ok".split()得到["INFO", "qwen2.5-7b", "ok"]Counter接受任何可迭代对象defaultdict(list)在访问未存在 key 时自动塞一个空 list
✅ 参考解法
写不出来再打开
from collections import Counter, defaultdict
log_lines = [
"INFO qwen2.5-7b ok",
"INFO llama3-8b ok",
"WARN qwen2.5-7b slow",
"INFO qwen2.5-7b ok",
"ERROR llama3-8b oom",
]
levels = Counter(line.split()[0] for line in log_lines)
print(levels)
by_model = defaultdict(list)
for line in log_lines:
parts = line.split()
by_model[parts[1]].append(line)
print(dict(by_model))
🔍 进阶思考
Counter 本质是 dict 的子类,Counter.most_common() 是内置的方法。生产代码里几乎所有“按某字段统计 top N”的场景都用它,比手写堆排序清爽太多。