启用 read_only=True 可避免95%内存爆炸,因其跳过样式/公式解析、用流式XML按需读取;但需改用 iter_rows()、禁用随机访问和 ws.max_row,并务必调用 wb.close() 释放ZIP句柄。

直接启用 read_only=True 就能避开 95% 的内存爆炸问题,但不是加个参数就万事大吉——关键在后续怎么读、读什么、以及哪些隐性行为会悄悄把你拖回 OOM。
为什么普通模式加载 50MB Excel 却吃掉 2.5GB 内存
openpyxl 默认把整个 .xlsx 解压后当 DOM 树来建模:每个 Cell 对象都带样式、公式、注释、超链接、字体、边框……哪怕单元格是空的,也得分配对象。一个 50MB 文件解压后 XML 内容可能超 1GB,再乘上 Python 对象开销,2.5GB 很常见。
更隐蔽的是:即使你只调用 ws['A1'].value,普通模式也会预加载整张表的结构信息(行列数、合并区域、条件格式等),这些元数据本身就很占内存。
而 read_only=True 模式下:
立即学习“Python免费学习笔记(深入)”;
- 不解析样式、公式、图表、注释等非值数据
- 不构建完整 Cell 对象,返回的是轻量级
ReadOnlyCell - 行/列遍历走流式 XML 解析器,按需拉取,不缓存整表
- 内存占用基本与当前处理行数成正比,而非文件大小
read_only=True 后必须改掉的 3 个写法
启用只读模式后,很多惯用写法会直接报错或静默失效:
-
不能用
ws['A1']或ws.cell(row=1, column=1)——ReadOnlyWorksheet不支持随机访问,只能用iter_rows()或iter_cols() -
不能读
.font、.border、.comment—— 这些属性根本没加载,访问会返回None或抛AttributeError -
不能依赖
ws.max_row/ws.max_column—— Excel 应用常写错尺寸元数据,要用ws.calculate_dimension()校验,出错时得手动ws.reset_dimensions()
正确姿势是:
wb = load_workbook('large.xlsx', read_only=True)
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True): # values_only=True 直接得 tuple,跳过对象构造
if not any(row): # 跳过全空行
continue
print(row[0], row[2])
wb.close() # 必须 close,否则底层 ZIP 文件句柄不释放
values_only=True 到底要不要加
这个参数决定你拿到的是原始值(str/int/datetime)还是带类型包装的 ReadOnlyCell 对象。
- 加
values_only=True:快、省内存、适合纯数据提取;但丢失日期/数字格式信息(比如 Excel 里显示2024-01-01,Python 可能变成datetime对象,也可能变float序号) - 不加:能通过
cell.data_type区分'n'(number)、'd'(date)、's'(string)等,但每次访问都要触发一次轻量解析,且仍无法还原样式
绝大多数 ETL 场景建议加,除非你明确需要区分“用户输的 100”和“Excel 公式算出来的 100.00”。
别忘了 close(),否则文件句柄一直挂着
load_workbook(..., read_only=True) 底层用的是 zipfile.ZipFile,它不会自动关闭。如果你循环处理上百个大文件却不 wb.close(),很快会遇到 OSError: [Errno 24] Too many open files。
安全写法是用上下文管理器(虽然 openpyxl 官方没原生支持,但可以自己包一层):
from contextlib import contextmanager
from openpyxl import load_workbook
<p>@contextmanager
def readonly_workbook(path):
wb = load_workbook(path, read_only=True)
try:
yield wb
finally:
wb.close()</p><h1>使用</h1><p>with readonly_workbook('data.xlsx') as wb:
ws = wb.active
for row in ws.iter_rows(values_only=True):
process(row)
最易被忽略的一点:只读模式下 ws 不是独立对象,wb.close() 才真正释放资源。很多人只记得关文件,却忘了关 Workbook 实例本身。


















