直接用 open() 读大文件会崩内存,因为 read() 或 readlines() 默认一次性加载全部内容到内存;流式分块读取应使用 iter(functools.partial(f.read, size), sentinel) 配合二进制模式与合适 chunk 大小(如 8kb–64kb)。

为什么直接用 open() 读大文件会崩内存
因为默认 read() 或 readlines() 会把整个文件一次性加载进内存。一个 2GB 的日志文件,可能直接吃光 8GB 内存,进程被系统 kill。流式分块读取的核心目标不是“看起来优雅”,而是让 for 循环每次只拿固定字节数或固定行数,不累积。
iter() 配合 functools.partial 的真实调用逻辑
这个组合的本质是:把「反复调用某个带固定参数的函数」包装成迭代器。iter(callable, sentinel) 会不断调用 callable,直到返回值等于 sentinel 才停止。而 functools.partial 用来冻结 file.read 的 chunk size 参数,避免每次手动传。
常见错误写法:iter(f.read, '') —— 这实际等价于 iter(f.read, b'')(二进制模式下),但 f.read() 在文件末尾返回空 bytes,所以能停;可一旦用文本模式打开,f.read() 返回空字符串 '',而 iter(f.read, '') 一上来就匹配哨兵,直接返回空迭代器。
正确做法必须明确区分模式:
- 二进制模式(推荐):
with open('big.bin', 'rb') as f: for chunk in iter(functools.partial(f.read, 8192), b''): process(chunk) - 文本模式(需换思路):不能直接用
f.read,得改用f.readline或自定义读行函数,因为f.read(n)在文本模式下可能在 UTF-8 多字节边界截断,引发UnicodeDecodeError
chunk size 设多少才合理
没有全局最优值,取决于你的 I/O 模式和处理逻辑:
- 太小(如 1):系统调用频繁,
read()开销压倒数据处理,CPU 花在上下文切换上 - 太大(如 100MB):单次分配大 buffer,GC 压力大,且失去“流式”意义
- 经验值:8KB–64KB 是多数场景的平衡点。SSD 上 64KB 接近页对齐;网络文件(如 S3 代理)可试 1MB,减少 HTTP 分块请求次数
- 注意:如果后续要按行解析(如 CSV、JSONL),别用
f.read(n),改用for line in f:—— 它内部已优化缓冲,且保证行完整
比 iter + partial 更安全的替代方案
这个组合容易踩坑,尤其新手混淆文本/二进制、哨兵值、异常传播。生产环境建议优先考虑:
- 逐行读(文本首选):
for line in open('data.txt'):—— 简洁、自动缓冲、不爆内存、行完整 - 标准库
io.BufferedReader:with open('big.bin', 'rb') as f: reader = io.BufferedReader(f, buffer_size=65536) while True: chunk = reader.read(8192) if not chunk: break process(chunk) - 第三方库如
smart-open(支持 S3/HDFS)或pandas.read_csv(chunksize=)(结构化数据)
真正需要手写 iter(functools.partial(...)) 的场景其实很少,一般是底层工具链要求纯函数式接口,或者你正在封装一个通用 chunk reader 工具函数。这时候务必加单元测试覆盖空文件、刚好整除、非整除、EOF 边界这几种 case。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











