Python 生成器入门:用 yield 写出省内存的数据处理流水线

举报
yd_257782158 发表于 2026/11/13 11:56:02 2026/11/13
【摘要】 处理大文件时,最常见的错误是把它一次性读进内存。一个几百兆的日志文件,用 readlines() 展开成列表,内存占用立刻翻倍,程序还没开始干活就先吃掉了大半资源。生成器正是为了解决这个问题而生的:它不急于算出全部结果,而是需要一个、产出一个,用多少、算多少。 一、yield:让函数学会“暂停”普通函数执行到 return 就会结束并销毁局部状态,而生成器函数使用 yield。每次执行到 y...

处理大文件时,最常见的错误是把它一次性读进内存。一个几百兆的日志文件,用 readlines() 展开成列表,内存占用立刻翻倍,程序还没开始干活就先吃掉了大半资源。生成器正是为了解决这个问题而生的:它不急于算出全部结果,而是需要一个、产出一个,用多少、算多少。

一、yield:让函数学会“暂停”

普通函数执行到 return 就会结束并销毁局部状态,而生成器函数使用 yield。每次执行到 yield,函数会把值交出去并原地暂停,保留全部局部变量;下一次调用 next(),它从暂停处继续执行。

def count_up(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for num in count_up(3):
    print(num)  # 依次输出 1、2、3

这里没有一次性生成 1 到 n 的列表,内存占用始终只有几个变量。for 循环能直接遍历生成器,就像遍历列表一样自然。

二、生成器表达式:一行写出惰性序列

列表推导式用方括号,生成器表达式把方括号换成圆括号,语义立刻从“立即算完”变成“按需计算”:

squares = (x * x for x in range(10**6))
print(next(squares))  # 只算出第一个值 0

一百万个平方数不会同时存在于内存里,取一个算一个。需要全部结果时再用 list() 展开,但那会失去惰性的意义,一般只在数据量确实很小时才这样做。

三、串联成流水线:日志处理实战

生成器的真正威力在于串联。每个环节只处理一行数据,整条流水线一次只在内存中保留少量数据:

def read_lines(path):
    with open(path, encoding='utf-8') as f:
        for line in f:
            yield line.strip()

def only_errors(lines):
    for line in lines:
        if 'ERROR' in line:
            yield line

def take_fields(lines):
    for line in lines:
        yield line.split(' ', 2)[-1]

for msg in take_fields(only_errors(read_lines('app.log'))):
    print(msg)

读文件、过滤、提取字段三步各自独立,组合时却像接水管一样自然。换掉其中任何一环,流水线其余部分都不用改;数据量从几兆涨到几个 G,内存占用几乎不变。

四、两个容易踩的坑

第一,生成器只能遍历一次。数据被消费后就消失了,需要重复使用时要重新创建,或者先用 list() 物化一份。第二,yield 和带值的 return 不能混用:在生成器函数里写带值的 return 会直接触发 StopIteration,初学者常把“返回值”当成“产出值”,结果丢了数据。

小结

生成器不是炫技的语法糖,而是一种组织数据处理的方式:把“一次性算完”改成“用多少算多少”。下次遇到大文件、长序列、无限数据流时,先想想能不能用 yield 搭一条流水线——内存会立刻轻松下来,代码也会比层层嵌套的循环清爽得多。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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