流式处理海量数据的核心是避免全量加载内存,而非仅用generator语法;必须用generator的三种oom前兆是:进程被系统killed、pandas.read_csv卡住超30秒且内存飙升、爬虫页数未知不敢预设上限。

能流式处理海量数据,核心不在“怎么写 generator”,而在“不把数据全读进内存”——只要还用 pandas.read_csv() 或 json.load(),再漂亮的 yield 也是纸糊的防火墙。
什么时候必须用 generator?看这三类 OOM 前兆
不是“数据大了才考虑”,而是出现以下任一现象,就该立刻切 generator:
- 进程被系统
Killed,dmesg | tail显示Out of memory: Kill process -
pandas.read_csv()卡住超过 30 秒,top里 Python 进程 RSS 内存持续飙升 - 爬虫中写
for page in range(1, 10000),但实际有效页数未知,不敢预设上限
yield 不是语法糖,是内存控制开关
写 generator 的关键不是记住 yield 关键字,而是理解它如何让执行暂停并保存上下文。下面这个日志解析函数,就是典型生产级写法:
def parse_nginx_log_stream(log_path):
with open(log_path, "r", encoding="utf-8") as f:
for line in f: # ✅ 每次只读一行,不加载全文
try:
ip = line.split()[0]
status = int(line.split('"')[2].split()[1])
if status == 404:
yield {"ip": ip, "status": status} # ✅ 只产出当前行所需字段
except (IndexError, ValueError):
continue # ✅ 跳过脏行,不中断流
注意三点:
- 不用
readlines(),用文件对象迭代器——底层调用f.readline(),内存占用恒定在 KB 级 -
yield后只传结构化字典,不传原始line字符串(避免引用残留导致内存无法释放) - 异常处理在循环内,单行失败不影响后续行产出——generator 天然支持容错流
别踩这些 generator 实操坑
很多 generator 写得看似正确,上线后照样 OOM,问题常出在“隐式加载”:
- 在
yield前调用了list(f)、df.to_dict("records")或json.loads(...)—— 这些操作会一次性吃光内存,yield已经晚了 - 用
itertools.chain(*[gen1(), gen2()])时,gen1()和gen2()被立即求值,失去懒加载意义;应改用itertools.chain.from_iterable([gen1(), gen2()]) - generator 对象被多次遍历(比如传给两个
sum()和list()),第二次调用会返回空——它是一次性消耗品,需重新调用生成函数
真正难的从来不是写出一个 yield,而是识别代码里所有偷偷把数据拖进内存的“暗桩”。比如 df.groupby().apply() 表面没读全表,但 groupby 本身会缓存分组键,大数据量下仍是隐患。generator 的价值,是逼你把每一步内存行为都摊开在阳光下。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











