用 Python 生成器处理大文件:不要一次把全部数据读进内存

举报
yd_268445051 发表于 2026/11/03 10:35:39 2026/11/03
【摘要】 用 Python 生成器处理大文件:不要一次把全部数据读进内存处理日志文件时,最常见的写法是 readlines() 或 read() 一次读入全部内容,再逐行处理。文件只有几 MB 时这没问题;一旦文件到了几百 MB,内存占用会跟着暴涨,轻则变慢,重则直接被系统杀掉。生成器就是为这种场景准备的:一次只产出一条数据,用完即丢,内存占用始终保持在一个很小的水平。 先看问题出在哪里# 危险写法...

用 Python 生成器处理大文件:不要一次把全部数据读进内存

处理日志文件时,最常见的写法是 readlines() 或 read() 一次读入全部内容,再逐行处理。文件只有几 MB 时这没问题;一旦文件到了几百 MB,内存占用会跟着暴涨,轻则变慢,重则直接被系统杀掉。生成器就是为这种场景准备的:一次只产出一条数据,用完即丢,内存占用始终保持在一个很小的水平。

先看问题出在哪里

# 危险写法:1GB 的日志会先完整进内存
with open("access.log", encoding="utf-8") as f:
    lines = f.readlines()
    for line in lines:
        if "ERROR" in line:
            print(line, end="")

readlines() 返回的是一个完整列表,文件多大,列表就多大。改成生成器后,函数每次只返回一行,调用方处理完一行再要下一行:

def error_lines(path):
    with open(path, encoding="utf-8") as f:
        for line in f:
            if "ERROR" in line:
                yield line

for line in error_lines("access.log"):
    print(line, end="")

这里的关键是 yield:函数执行到 yield 就暂停并交出当前值,下一次迭代才从暂停处继续。文件对象本身就是可迭代的,逐行读本来就不会一次性加载全文件,生成器只是把“过滤”这一步也包进了惰性流水线。

生成器可以串联成流水线

真正好用的是把多个处理步骤串起来,每一步都是生成器,数据像流水一样一站站往下走,中间不产生任何完整列表:

def read_lines(path):
    with open(path, encoding="utf-8") as f:
        yield from f

def parse(line):
    parts = line.split()
    return parts[0], parts[6], parts[8]  # IP、路径、状态码

def only_errors(rows):
    for ip, path, status in rows:
        if status.startswith("5"):
            yield ip, path, status

for ip, path, status in only_errors(map(parse, read_lines("access.log"))):
    print(ip, path, status)

map 本身也是惰性的,所以整条链路在任何时刻内存里都只有当前这一行。需要加一步处理时,再写一个生成器函数插进去即可,不用改上下游。

三个容易踩的坑

第一,生成器只能消费一次。把它转成列表、或用 for 循环跑完一遍之后,第二次迭代什么也得不到。需要复用结果时,要么重新调用生成器函数,要么在确实需要时才转成列表。

第二,不要在生成器里藏“打开后不关闭”的资源。上面的例子把 with open(...) 写在生成器函数内部,生成器被垃圾回收或提前退出时文件不一定立刻关闭;稳妥的做法是在调用方用 with 包住整个消费过程,或者确保消费到底。

第三,生成器不是万能药。它解决的是“数据量大、处理可逐条进行”的问题;如果业务需要随机访问、反复排序或多次统计,惰性流反而会让代码变复杂,这时先读入内存或用数据库才是对的工具。

判断标准很简单:如果处理逻辑是“读一条、算一条、输出一条”,就用生成器;如果要“先看完全部数据再决定”,就别硬套。把大文件拆成小步处理,程序在数据规模翻十倍时依然稳得住,这才是生成器真正的价值。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。