当前位置: 首页 > news >正文

Day 20:【99天精通Python】迭代器与生成器 - 内存优化的黑科技

Day 20:【99天精通Python】迭代器与生成器 - 内存优化的黑科技

前言

欢迎来到第20天!

在处理数据时,我们经常会遇到这样的场景:需要处理一个几 GB 甚至几 TB 的大文件,或者需要生成一个包含 1 亿个数字的列表。
如果直接把所有数据一次性加载到内存中(比如用列表),电脑内存瞬间就会爆满,程序卡死。

Python 给我们提供了一套优雅的解决方案:迭代器 (Iterator)生成器 (Generator)。它们的核心思想是:“用一个,拿一个”,而不是**“一次性全拿出来”**。这就像是流水线作业,既节省内存,又提高了效率。

本节内容:

  • 可迭代对象 (Iterable) vs 迭代器 (Iterator)
  • for循环的本质
  • yield关键字详解
  • 生成器函数与生成器表达式
  • 实战:斐波那契数列与大文件读取

一、迭代器:按需获取

1.1 什么是可迭代对象 (Iterable)?

简单说,凡是能用for循环遍历的东西,都是可迭代对象。
例如:列表、元组、字符串、字典、集合。

fromcollections.abcimportIterableprint(isinstance([],Iterable))# Trueprint(isinstance("abc",Iterable))# Trueprint(isinstance(100,Iterable))# False (整数不可迭代)

1.2 什么是迭代器 (Iterator)?

迭代器是一个"更高级"的对象,它不仅可以被遍历,还记录了当前访问到了哪里
它必须实现两个方法:

  1. __iter__(): 返回迭代器对象本身。
  2. __next__(): 返回下一个元素。如果没有元素了,抛出StopIteration异常。

1.3 iter() 和 next()

我们可以用iter()函数把一个列表变成迭代器。

nums=[1,2,3]it=iter(nums)# 创建迭代器print(next(it))# 1print(next(it))# 2print(next(it))# 3# print(next(it)) # 报错: StopIteration

1.4 for 循环的本质

其实for循环内部就是在做这件事:

  1. 调用iter(obj)获取迭代器。
  2. 不断调用next(it)获取元素。
  3. 遇到StopIteration异常时,停止循环。

二、生成器 (Generator):最简单的迭代器

手动写一个迭代器类太麻烦了(需要写__iter____next__)。Python 提供了生成器,让你用写函数的语法来实现迭代器。

2.1 yield 关键字

yield的作用类似于return,但它不会结束函数,而是暂停函数,并保存当前的状态。下次调用next()时,函数会从暂停的地方继续执行。

defmy_generator():print("开始生成...")yield1print("暂停回来,继续生成...")yield2print("最后一次...")yield3gen=my_generator()# 此时函数并没有执行!只有调用 next() 才会动。print(next(gen))# 输出:# 开始生成...# 1print(next(gen))# 输出:# 暂停回来,继续生成...# 2

2.2 生成器表达式

类似于列表推导式,只是把方括号[]换成了圆括号()

# 列表推导式 (立即生成所有数据,占内存)list_data=[x**2forxinrange(10)]print(list_data)# [0, 1, 4, ..., 81]# 生成器表达式 (不生成数据,只保存算法,省内存)gen_data=(x**2forxinrange(10))print(gen_data)# <generator object ...># 必须遍历才能取值fornumingen_data:print(num,end=" ")

三、生成器 vs 列表:性能对比

假设我们要处理 1000 万个数字。

importsys# 方式1:列表# 瞬间占用大量内存big_list=[xforxinrange(10000000)]print(f"列表占用内存:{sys.getsizeof(big_list)/1024/1024:.2f}MB")# 结果约 380 MB# 方式2:生成器# 几乎不占内存big_gen=(xforxinrange(10000000))print(f"生成器占用内存:{sys.getsizeof(big_gen)}Bytes")# 结果只有 100多 Bytes (无论数据多大,它都只占这么多)

结论:处理海量数据时,必须使用生成器。


四、实战练习

练习1:斐波那契数列生成器

斐波那契数列:1, 1, 2, 3, 5, 8, 13…
如果用递归算第100位会卡死,用生成器则轻松秒杀。

deffib_generator(n):"""生成前 n 个斐波那契数"""a,b=0,1count=0whilecount<n:yieldb# 产出当前数值a,b=b,a+b count+=1# 打印前10个fornuminfib_generator(10):print(num,end=" ")# 1 1 2 3 5 8 13 21 34 55

练习2:大文件读取器

假设有一个 10GB 的日志文件,每行一条日志。我们需要查找包含 “ERROR” 的行。
直接readlines()会内存溢出,我们用生成器逐行读取。

defread_large_file(filename):"""生成器:每次只读一行"""withopen(filename,"r",encoding="utf-8")asf:forlineinf:yieldline# 暂停,把行给出去# 使用# 假设 log.txt 存在# for line in read_large_file("log.txt"):# if "ERROR" in line:# print(f"发现错误: {line.strip()}")

:其实 Python 的文件对象本身就是可迭代的,直接for line in f效果也是一样的,这里只是为了演示原理。


五、深入理解:send() 方法

生成器不仅可以产出数据 (yieldout),还可以接收数据 (yieldin)。

defeater():print("准备吃饭...")whileTrue:food=yield# 接收外部传进来的值print(f"吃了{food}")e=eater()next(e)# 预激生成器 (执行到第一个 yield 并暂停)e.send("包子")# 吃了 包子e.send("面条")# 吃了 面条e.close()# 关闭生成器

这是协程 (Coroutine)的雏形,也是 Python 异步编程 (async/await) 的基石。


六、常见问题

Q1:生成器能遍历第二次吗?

不能。生成器是"一次性"的。遍历完一遍后,指针就在最后了,再调用next()会抛错。如果需要再次遍历,必须重新创建生成器对象。

Q2:return在生成器里起什么作用?

在生成器函数中,return等同于抛出StopIteration异常,用于终止生成。


七、小结

迭代 Iteration

Iterable (可迭代对象)

Iterator (迭代器)

列表, 字符串...

实现了iter

实现了iternext

next() 取值

只能往前,不能回头

Generator (生成器)

函数中包含 yield

生成器表达式 (x for x in ...)

省内存,惰性计算

关键要点

  1. Iterable是原材料,Iterator是流水线。
  2. 生成器是最简单的迭代器写法。
  3. yield是暂停键,next是播放键。
  4. 处理大量数据时,优先考虑生成器。

八、课后作业

  1. 自定义 range:编写一个生成器函数my_range(start, end, step),模仿 Python 内置range的功能(支持浮点数步长)。
  2. 素数生成器:编写一个生成器,无限生成素数(2, 3, 5, 7…)。并在外部循环中打印前 20 个素数。
  3. 日志清洗:模拟一个包含脏数据的列表data = ["info: ok", "error: fail", None, "", "warn: check"]。编写一个生成器,清洗掉None和空字符串,并统一转换为大写。

下节预告

Day 21:基础篇总结与综合实战- 恭喜!你已经完成了 Python 基础篇的所有核心内容。明天我们将通过一个综合性的图书管理系统项目,把这20天的知识串联起来!


系列导航

  • 上一篇:Day 19 - 装饰器
  • 下一篇:Day 21 - 基础篇总结与实战(待更新)
http://www.jsqmd.com/news/231657/

相关文章:

  • ResNet18实战教程:农业作物识别系统搭建
  • ResNet18技术揭秘:轻量级模型设计哲学
  • 01.学习预备
  • ResNet18部署优化:模型并行推理技术
  • 详解PCB板生产厂家在样板打样阶段的配套支持
  • ResNet18部署案例:智能家居控制中心
  • ResNet18实战:无人机航拍图像分析系统搭建
  • ResNet18实战教程:多场景物体识别应用开发
  • ResNet18性能对比:ResNet18 vs ResNet50实测
  • TheIsle恐龙岛巨龙服1.53服务器搭建代码
  • ResNet18实战指南:医疗影像预处理技巧
  • Multisim14与NI Ultiboard联合设计中的元器件匹配问题解析
  • 数字时钟电路设计:基于Multisim仿真电路图的新手教程
  • ResNet18部署避坑指南:常见错误及解决方案
  • ResNet18性能测试:不同光照条件下的识别效果
  • 【阅读笔记】Bayer阵列坏点校正-《Adaptive pixel defect correction》
  • 【随笔】十年之约,不止约定十年
  • ResNet18技术揭秘:为何成为经典CNN架构
  • 项目应用中Vivado 2023.1多用户License管理策略
  • ResNet18入门指南:快速理解1000类分类
  • 识别正版Amlogic固件下载官网:核心要点快速理解
  • Multisim中实现克拉泼振荡电路自激过程可视化详解
  • ResNet18应用开发:智能零售库存管理系统
  • 验证文件无法访问问题排查手册
  • ResNet18部署指南:企业级图像识别方案搭建
  • ResNet18入门教程:ImageNet预训练模型使用
  • ResNet18技术解析:多类别分类任务实现方法
  • ResNet18性能对比:与其他轻量级模型的差异
  • Java基于微信小程序的高校课堂教学管理系统,附源码+文档说明
  • 一文说清继电器模块与单片机连接的电路图分析