尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python生成器实战:用yield流式处理超大文件,内存占用直降90%
文章目录环境信息前言800MB的CSV文件直接 read 出来内存爆了一、生成器原理yield 到底做了什么二、生成器表达式一行写法的陷阱三、实战场景1流式读取大CSV四、实战场景2流式解析大JSON五、实战场景3API分页拉取六、量化验证生成器到底省了多少内存七、三个使用边界八、总结环境信息项目版本/说明Python3.10标准库csv / json / itertools无需第三方内存监控tracemalloc标准库测试场景大CSV文件 / 大JSON文件 / 分页API前言800MB的CSV文件直接 read 出来内存爆了上个月我处理香港政府开放数据平台data.gov.hk的一个交通流量数据集——800MB 的 CSV几百万行记录。我的第一版代码是这样的importcsv# ❌ 危险写法一次性把所有行读进内存withopen(traffic_flow_800mb.csv,r,encodingutf-8)asf:rowslist(csv.reader(f))# 800MB 数据全部加载进内存print(f加载了{len(rows)}行)# 然后内存就爆了——list 里存了几百万个 list 对象实际内存占用远超 800MB跑起来之后机器直接卡死。因为list(csv.reader(f))会把每一行都转换成一个 Python list 对象几百万个 list 的开销叠加起来实际内存占用是文件大小的好几倍。后来我把代码改成了生成器写法importcsv# ✅ 生成器写法逐行处理内存占用恒定defread_csv_rows(filepath):withopen(filepath,r,encodingutf-8)asf:readercsv.reader(f)forrowinreader:yieldrow# 每次只返回一行不积累fori,rowinenumerate(read_csv_rows(traffic_flow_800mb.csv)):# 处理这一行...ifi%1000000:print(f已处理{i}行)同样的功能内存占用从文件大小的数倍降到恒定的几十KB。这就是生成器的力量。收藏提示①处理大文件的第一原则——永远不要list()一个文件读取器。用yield逐行返回内存占用恒定。文末有3个开箱即用的生成器实战。一、生成器原理yield到底做了什么要理解为什么生成器省内存先理解它和普通函数、普通列表的区别。普通函数调用后一次性执行完用return返回结果然后函数就结束了。生成器函数函数体里有yield关键字。调用它不会立即执行函数体而是返回一个生成器对象。每次对这个生成器调用next()函数才执行到下一个yield处把yield后面的值吐出来然后暂停。defsimple_generator():print(开始)yield1# 第一次 next() 执行到这里返回1暂停print(继续)yield2# 第二次 next() 执行到这里返回2暂停print(结束)# 第三次 next() 会抛出 StopIterationgensimple_generator()print(next(gen))# 输出开始然后返回 1print(next(gen))# 输出继续然后返回 2print(next(gen))# 输出结束然后抛出 StopIteration关键就在于暂停这两个字。生成器不会一次性把所有值算出来存着而是用到哪个算哪个。这就是惰性求值lazy evaluation。对比一下# 列表一次性生成100万个数字全部存内存nums_list[iforiinrange(1000000)]# 内存占用约 36MB每个int对象28字节 × 100万# 生成器只是定义了一个规则不实际生成nums_gen(iforiinrange(1000000))# 内存占用约 100字节就一个生成器对象同样是 100 万个数字列表占 36MB生成器只占 100 字节。这就是内存直降 90%“的真正来源——不是魔法是不预先计算、用到才算”。二、生成器表达式一行写法的陷阱注意上面nums_gen (i for i in range(1000000))用的是圆括号这是生成器表达式。而列表推导式用的是方括号。# 列表推导式方括号—— 立即生成占内存squares_list[x*xforxinrange(1000000)]# 生成器表达式圆括号—— 惰性不占内存squares_gen(x*xforxinrange(1000000))一个常见的坑生成器表达式只能迭代一次。因为它不是存着结果而是边算边吐吐完就没了gen(x*xforxinrange(5))print(list(gen))# [0, 1, 4, 9, 16]print(list(gen))# [] —— 已经耗尽第二次是空的如果你需要多次遍历要么用列表要么用itertools.tee要么重新创建生成器。三、实战场景1流式读取大CSV回到开头的大文件场景把流式读取封装成一个可复用的生成器importcsvdefstream_csv(filepath,skip_headerTrue): 流式读取大CSV逐行yield不占内存 skip_header: 是否跳过表头行 withopen(filepath,r,encodingutf-8)asf:readercsv.DictReader(f)# DictReader 返回字典键是表头forrowinreader:yieldrow# 使用逐行处理配合条件过滤forrowinstream_csv(traffic_flow_800mb.csv):# 只处理屯门区的数据ifrow.get(district)屯門:process(row)如果还要做过滤 转换可以链式组合生成器deffilter_by_district(rows,district):过滤生成器只保留指定区forrowinrows:ifrow.get(district)district:yieldrowdefextract_columns(rows,columns):转换生成器只提取需要的列forrowinrows:yield{col:row.get(col)forcolincolumns}# 链式组合三个生成器串成流水线all_rowsstream_csv(traffic_flow_800mb.csv)tuen_mun_rowsfilter_by_district(all_rows,屯門)cleaned_rowsextract_columns(tuen_mun_rows,[time,flow,station])forrowincleaned_rows:print(row)这就是生成器流水线——每一步都是惰性的数据像水流一样从上一个生成器流到下一个全程不积累。无论文件多大内存占用恒定。四、实战场景2流式解析大JSONJSON 比 CSV 麻烦因为标准库的json.load()会把整个文件读进内存。但可以用ijson库第三方或者逐条解析 JSON Lines 格式importjsondefstream_jsonl(filepath): 流式读取 JSON Lines 格式每行一个JSON对象 香港政府部分API返回的就是这种格式 withopen(filepath,r,encodingutf-8)asf:forlineinf:lineline.strip()ifline:# 跳过空行yieldjson.loads(line)# 使用forrecordinstream_jsonl(records.jsonl):ifrecord.get(type)transaction:process(record)收藏提示②如果遇到的是单个超大 JSON 数组[{...},{...},...]标准库json.load()会全量加载。要么让数据提供方改 JSON Lines 格式要么用ijson库做增量解析。JSON Lines 是流式处理的友好格式设计数据管道时优先选它。五、实战场景3API分页拉取调用分页 API 时生成器可以优雅地隐藏分页逻辑importrequestsdeffetch_paginated(url,page_size100,max_pagesNone): 分页拉取API数据逐页yield对外表现为一个连续的流 page1whileTrue:ifmax_pagesandpagemax_pages:breakresprequests.get(url,params{page:page,size:page_size})dataresp.json()recordsdata.get(records,[])ifnotrecords:# 没有更多数据breakforrecordinrecords:yieldrecord page1# 使用调用方完全不用关心分页逻辑forrecordinfetch_paginated(https://api.data.gov.hk/v1/records):process(record)六、量化验证生成器到底省了多少内存口说无凭用tracemalloc实测一下标准库自带的内存追踪工具importtracemallocdefprocess_list(n1000000):列表方式全量加载tracemalloc.start()data[i*2foriinrange(n)]totalsum(data)current,peaktracemalloc.get_traced_memory()tracemalloc.stop()returnpeak/1024/1024# 转MBdefprocess_generator(n1000000):生成器方式惰性处理tracemalloc.start()totalsum(i*2foriinrange(n))# 注意这是生成器表达式current,peaktracemalloc.get_traced_memory()tracemalloc.stop()returnpeak/1024/1024list_mbprocess_list()gen_mbprocess_generator()print(f列表方式峰值内存:{list_mb:.1f}MB)print(f生成器方式峰值内存:{gen_mb:.1f}MB)print(f内存节省:{(1-gen_mb/list_mb)*100:.0f}%)输出示例列表方式峰值内存: 36.2 MB 生成器方式峰值内存: 0.1 MB 内存节省: 99%收藏提示③生成器不是更快而是更省内存。在CPU上生成器因为惰性调用有时反而略慢。它的核心价值是——让你能处理大到放不进内存的数据。如果数据量小到能全放内存列表反而更简单直接。七、三个使用边界生成器不是银弹有三个场景要谨慎需要随机访问生成器只能顺序迭代不能data[100]这样随机取。如果需要反复随机访问还是用列表。需要多次遍历生成器耗尽就没了。如果需要遍历两遍要么存成列表要么用itertools.tee要么重建生成器。数据量小如果数据只有几百条生成器和列表没区别反而增加了代码复杂度。大文件才值得用生成器小数据直接用列表。八、总结生成器的核心就一句话惰性求值——用到哪个算哪个不预先全部算出来。原理yield让函数暂停而不是结束配合next()逐步取值写法生成器函数yield和生成器表达式圆括号两种价值处理大文件/大JSON/分页API时内存从文件大小数倍降到恒定几十KB边界不能随机访问、只能遍历一次、小数据没必要用这篇是 Python 进阶系列的第三篇——0814 写了正则和装饰器这篇写生成器。三个主题的共同点都是写了很久 Python 但可能没真正理解的基础进阶。正则解决格式校验装饰器解决代码复用生成器解决内存瓶颈。本文为 Python 生成器技术分享。内存对比数据通过 tracemalloc 实测不同环境数值略有差异但生成器省内存的结论是确定的。香港政府数据示例为演示场景实际数据集以 data.gov.hk 为准。
RELATED

相关推荐

AI智能体可视化监控:从Token消耗到技能进化的全链路追踪实践

AI智能体可视化监控:从Token消耗到技能进化的全链路追踪实践

1. 项目概述:从“黑盒”到“白盒”的智能体进化 如果你最近在折腾AI智能体,尤其是像Hermes Agent这类能联网、能调用工具、能处理复杂任务的开源项目,那你一定经历过这种场景:任务跑起来了,终端里日志刷刷地过&#xf…

📅 2026/9/29 14:44:29
老游戏联机老是翻车?开源翻译官 IPXWrapper 让它们在 Windows 11 上重新开口

老游戏联机老是翻车?开源翻译官 IPXWrapper 让它们在 Windows 11 上重新开口

老游戏联机老是翻车?开源翻译官 IPXWrapper 让它们在 Windows 11 上重新开口 【免费下载链接】ipxwrapper 项目地址: https://gitcode.com/gh_mirrors/ip/ipxwrapper 周五晚上,那场没连上的红警2 周五晚上,老同学群里有人喊话&#…

📅 2026/9/29 4:49:08
MiniMax H3 V4 Turbo、Light2V与Bernini:AI图像生成降本增效实战指南

MiniMax H3 V4 Turbo、Light2V与Bernini:AI图像生成降本增效实战指南

1. 这篇文章真正要解决的问题 如果你最近在关注AI图像生成领域,可能会被各种“最强模型”、“秒级出图”的宣传搞得眼花缭乱。特别是当MiniMax发布了H3 V4 Turbo、Light2V 4步加速和Bernini二采放大这一系列更新后,很多开发者和技术爱好者都想知道&#…

📅 2026/10/6 1:23:19
MORE NEWS

更多资讯

📰

工业自动化FAT检验表:87项硬核测试与四层验证逻辑

简介:本资源是一份完整的出厂验收测试(FAT)标准化检验表,面向自动化、过程控制、工业系统集成领域的工程师、质量检验人员及项目交付负责人,用于规范设备出厂前的功能性、安全性与合规性验证流程。文档覆盖文件审查、软…

📰

电商社群KPI失效真相:从静态考核表到动态作战地图

简介:本资源是一份面向电商运营管理者、社群负责人及绩效考核设计者的实操型KPI方案工具包,聚焦解决“如何科学设定社群运营考核指标”这一核心痛点。方案深度剖析转化率、复购率等结果型KPI与活跃度、活动频次等过程型KPI的适用边界与潜在风险&#xff…

📰

纠删码CPU开销实测:RustFS对比三副本,成本与性能权衡

这两年存储圈子里有一个话题每隔一阵就会被翻出来吵一轮:对象存储到底该用三副本还是纠删码?每次有人晒出EC方案的成本对比图,总会有一批人跳出来说“省那点钱,CPU都烧没了”,另一批人则用大厂案例反驳。我也一直想搞清…

📰

多Agent协作编排实战:事件驱动架构与共享上下文设计

如果你也发现单个Agent跑起来很像样,但一旦上了规模就乱成一锅粥,那这篇应该能帮到你。最近团队内部把多Agent协作的编排层项目收了个尾,代号就叫“Agent-Reach”,核心解决的是“如何让不同职能的Agent互相感知、彼此触达、协同干…

📰

纠删码不是免费午餐:RustFS实测EC与三副本的CPU代价

存储圈子这几年有个绕不开的话题:想省钱,能不能用纠删码(EC)替代多副本。尤其是我在折腾RustFS——一个用Rust写的轻量分布式存储系统——这套逻辑一度让我很上头:同样是容忍两块盘同时故障,三副本要烧掉30…

📰

汇编核心:MOV指令与PUSH/POP栈操作,理解程序底层数据流动

直接进入正题。最近复习汇编语言,正好啃到“访问信息”这一节的后半部分,也就是 3.4.3 数据传送示例和 3.4.4 压入和弹出栈数据。这两个小节表面上是两个独立话题,一个讲 MOV 指令怎么搬运数据,一个讲 PUSH/POP 怎么操作栈&#x…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬