应使用 defaultdict(list) 构建倒排索引,但需封装屏蔽无效查询;分词须小写化、去标点、过滤短词,避免 str.split();内存优化用 array.array 或 roaringbitmap;and/or 查询宜用双指针归并或位运算。

倒排索引该用 dict 还是 defaultdict?
直接用 dict 会频繁触发 KeyError,每次插入新词都要手动检查键是否存在;用 defaultdict(list) 更省心,但要注意它会在访问任意未定义键时自动创建空列表——这在构建阶段没问题,但在后续查询时若误查拼错词,可能掩盖逻辑错误。实际中建议用 defaultdict(list) 构建,但上线前加一层封装,屏蔽对不存在词的无意义访问。
构建时典型写法:
from collections import defaultdict
inverted_index = defaultdict(list)
for doc_id, text in documents.items():
for word in tokenize(text):
inverted_index[word].append(doc_id)
分词环节为什么不能只靠 str.split()?
str.split() 无法处理标点、大小写、缩写(如 "don't")、Unicode 符号(如中文、emoji),会导致同一词被拆成多个变体。必须引入轻量分词逻辑:小写化 + 去标点 + 可选词干提取(如用 nltk.stem.PorterStemmer)。不推荐直接上 jieba 或 spaCy——除非你真要支持多语言或实体识别,否则它们启动慢、内存开销大,对千级文档纯文本搜索属于杀鸡用牛刀。
一个够用的清洗函数示例:
import re
def tokenize(text):
words = re.findall(r'\b[a-zA-Z]+\b', text.lower())
return [w for w in words if len(w) > 1]
如何避免内存爆炸?
倒排索引本质是“词 → 文档ID列表”,当文档量达十万级、词汇量超百万时,list 存储文档ID会浪费大量内存(尤其高频词如 “the”、“我”)。改用 array.array('I')(32位无符号整数)可节省约50%空间;更进一步,对长列表启用差分编码 + bytearray 压缩(如用 pyroaring 的 RoaringBitmap),能将内存压到原 list 的1/10以下。但注意:压缩结构查询稍慢,且不支持随机访问——如果你需要快速取第N个匹配文档ID,就得权衡。
关键取舍点:
- 文档总数 list,简单可靠
- 文档总数 10k–100k → 改用
array.array('I') - 文档总数 > 100k 且内存敏感 → 引入
RoaringBitmap,但需接受额外依赖和序列化成本
查询时 AND/OR 如何高效合并结果?
纯 Python 写集合交并(set.intersection() / set.union())在数据量大时很慢,因为要构造新集合、拷贝元素。更优做法是利用倒排列表已排序的特性,用双指针归并:对两个升序 array.array,一次遍历完成交集;OR 同理。若用了 RoaringBitmap,直接调 rb1 & rb2 和 rb1 | rb2 即可,底层是高度优化的位运算。
AND 归并示例(无额外内存分配):
def intersect_sorted(a, b):
i = j = 0
result = []
while i
真正麻烦的是短语查询(比如 “machine learning” 相邻出现)和位置信息维护——那得存每个词在文档内的偏移,索引体积立刻翻3倍以上,而且查询逻辑复杂度跃升。除非业务明确要求,否则先别碰。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











