直接用 open().readlines() 会爆内存,因为它将整个文件一次性加载进内存,处理大文件时易触发 memoryerror 或被 oom killer 杀死;应改用分块读取生成器或 pandas 的 chunksize 参数。

为什么直接用 open().readlines() 会爆内存
因为 readlines() 会把整个文件一次性加载进内存,哪怕只是读取几 GB 的日志或 CSV,Python 进程也会瞬间吃光可用 RAM,触发 MemoryError 或被系统 OOM Killer 杀掉。这不是代码写错了,是设计使然——它本就不是为大文件准备的。
常见错误现象:MemoryError、程序卡死无响应、系统变卡、ps aux 显示 Python 进程 RSS 占用飙升到数 GB。
- 适用场景:文本文件(如日志、CSV、JSONL)、行结构清晰、无需随机访问整行内容
- 关键原则:每次只 hold 住一块数据,处理完立刻丢弃,不让上一块残留引用
- 注意:别在生成器里累积结果到列表,比如
list(generator)或[line for line in gen]—— 这等于又回到原点
用 yield 写一个安全的分块行读取生成器
核心是控制每次从文件读多少行,而不是全读。用 itertools.islice 配合 open() 的迭代器接口最稳,避免手动计数出错。
def read_large_file(filepath, chunk_size=10000):
with open(filepath, 'r', encoding='utf-8') as f:
while True:
lines = list(itertools.islice(f, chunk_size))
if not lines:
break
yield lines
使用示例:
for chunk in read_large_file('access.log'):
# 每次 chunk 是一个包含最多 10000 行字符串的 list
processed = [line.strip().split() for line in chunk if line.strip()]
# 做你想做的处理,比如统计、过滤、写入新文件
# 不要在这里 append 到全局列表!
-
chunk_size不是越大越好:太大会增加单次内存峰值;太小会频繁 I/O,一般 1k–10k 行较平衡 - 必须用
with open:确保文件句柄及时释放,否则可能遇到Too many open files - 别在生成器外保留对
chunk的强引用,尤其别存进全局 list 或 dict
处理 CSV 大文件时,为什么不能直接用 pandas.read_csv()
默认情况下 read_csv() 会尝试推断全量数据类型、加载全部列、构建完整 DataFrame —— 这比 readlines() 更吃内存。即使加了 nrows,也只限制总行数,不解决单次加载压力。
正确做法是用 chunksize 参数让它返回一个可迭代的 TextFileReader:
for chunk in pd.read_csv('huge.csv', chunksize=5000):
# chunk 是一个 DataFrame,只含当前批次
result = chunk.groupby('category')['value'].sum()
# 累加到外部变量,或写入磁盘,不要拼接 chunk
- 切忌:
df = pd.concat([chunk for chunk in reader])—— 又爆了 - 如果只需某几列,务必加
usecols=['col_a', 'col_b'],跳过无关字段解析 - 指定
dtype(如{'id': 'int32', 'flag': 'category'})能显著减少内存占用和类型推断开销
生成器里做耗时处理,怎么避免阻塞和状态泄漏
生成器本身不执行逻辑,只提供数据流。真正处理逻辑若含状态(如累计计数、缓存前一行),容易在多次 yield 后出现意外行为。
- 状态变量必须定义在生成器函数内部,而不是外层作用域 —— 否则多个生成器实例会共享同一份状态
- 避免在生成器中做网络请求、数据库写入等阻塞操作;如必须做,考虑用
concurrent.futures.ThreadPoolExecutor异步提交,但注意线程安全与资源上限 - 如果需要“带上下文”的处理(如判断连续多行是否满足某种模式),推荐改用自定义迭代器类,比闭包 + 生成器更易维护边界
最常被忽略的一点:生成器一旦被遍历完毕(或中途抛异常未捕获),文件句柄会随 with 自动关闭;但如果你用 next(gen) 手动调用且没走完,文件不会关 —— 务必用 try/finally 或上下文管理器包装调用侧。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











