直接用 open() 读大文件会崩内存,因为 file.read() 或 list(file) 将整个文件一次性载入内存,导致 OOM 被系统 kill;安全做法是用 yield 按行或按块惰性读取,避免缓存历史数据、不提前解析、确保资源及时释放。

为什么直接用 open() 读大文件会崩内存?
因为 file.read() 或 list(file) 会把整个文件一次性载入内存。一个 3GB 的文本文件,UTF-8 编码下可能生成超过 3GB 的 str 对象,Python 进程直接 OOM —— 不是报错,是被系统 kill。
用 yield 按行读取的最小安全写法
核心是让生成器每次只产出一行(或一块),不缓存历史内容。关键点不是“用了 yield”,而是“没额外 hold 住数据”:
def read_large_file(filepath):
with open(filepath, encoding='utf-8') as f:
for line in f: # ← 这里 f 是迭代器,底层用缓冲但不加载全量
yield line.rstrip('\n\r')
-
for line in f本身已惰性,yield只是把它暴露给调用方,不增加内存负担 - 避免
yield line.strip():如果某行首尾有大量空白,strip()会新建字符串,且无法控制长度;rstrip('\n\r')更轻量、确定性更强 - 别在生成器里做复杂解析(如
json.loads(line))——失败时整行丢失,且异常堆栈难定位;先 yield 原始行,后处理
按固定字节数块读取(跳过换行边界问题)
当文件不含明确行分隔(如二进制日志、自定义格式),或某行超百MB时,按行读会卡死。此时需手动控制缓冲区:
def read_in_chunks(filepath, chunk_size=8192):
with open(filepath, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
-
chunk_size建议设为 4096–65536:太小(如 1)导致 syscall 过多;太大(如 1MB)可能单次分配压力大 - 必须用
'rb'模式:文本模式在遇到非法 UTF-8 字节时会抛UnicodeDecodeError,且无法跨块解码 - 若需按逻辑记录解析(如每条记录以
\x00结尾),得在 yield 后拼接上一块末尾 + 下一块开头,生成器本身不负责跨块状态维护
常见陷阱:看似惰性,实则偷偷吃内存
这些写法会让生成器“假惰性”,实际仍加载全量:
立即学习“Python免费学习笔记(深入)”;
-
return (line for line in open(...)):文件对象未关闭,且生成器退出时资源不释放 -
yield from open(...):同上,且无法控制编码和换行处理 - 在生成器内构建列表:
lines = []; for line in f: lines.append(...); yield from lines—— 直接回到内存爆炸原点 - 用
pandas.read_csv(..., chunksize=)却调用list(...):chunksize 控制的是每次返回的 DataFrame 大小,但list()仍会把所有 chunk 收集起来
真正按需的关键,是每次 yield 后函数挂起,变量作用域销毁,GC 能立刻回收上一轮局部对象 —— 这点容易被闭包、日志、异常捕获等意外打断。


















