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

Python进阶必备:彻底搞懂迭代器(Iterator)和生成器(Generator)

很多Python学习者学到这里就卡住了——iter()next()yield、生成器表达式……这些概念绕来绕去,到底有什么区别?什么时候该用哪个?今天一篇文章给你彻底讲透。

一、先来回答一个灵魂拷问

:为什么有了列表(list)、元组(tuple)这些容器,还需要迭代器和生成器?

:因为内存

看个例子:

# 一个包含1亿个数字的列表nums_list=[iforiinrange(100000000)]# 占用约800MB内存,直接撑爆

如果换成生成器:

# 生成器:惰性计算,一次只产生一个值nums_gen=(iforiinrange(100000000))# 几乎不占内存

这就是迭代器和生成器存在的最大意义——惰性求值(Lazy Evaluation),需要的时候才计算,不需要的时候不占内存。


二、迭代器(Iterator)—— 一个可以“记住遍历位置”的对象

1. 什么是迭代器?

迭代器是一个可以记住遍历位置的对象。它从第一个元素开始访问,直到所有元素被访问完结束。迭代器只能往前走,不能后退。

核心特点

  • 迭代器是一个对象,实现了__iter__()__next__()两个方法
  • 通过next()函数可以逐个获取元素
  • 当没有元素时,抛出StopIteration异常

2. 可迭代对象(Iterable) vs 迭代器(Iterator)

很多初学者分不清这两个概念:

概念定义举例
可迭代对象(Iterable)实现了__iter__()方法的对象,可以用for循环遍历list、tuple、dict、str、set
迭代器(Iterator)实现了__iter__()__next__()方法的对象,可以记住遍历位置iter(list)的返回值

简单理解:可迭代对象是“可以卖票的体育馆”,迭代器是“正在排队入场的人群”。体育馆(可迭代对象)可以多次生成新的队伍(迭代器)。

# 判断是否为可迭代对象 / 迭代器fromcollections.abcimportIterable,Iterator my_list=[1,2,3]print(isinstance(my_list,Iterable))# True → 可迭代print(isinstance(my_list,Iterator))# False → 不是迭代器my_iterator=iter(my_list)print(isinstance(my_iterator,Iterable))# Trueprint(isinstance(my_iterator,Iterator))# True

3.iter()next()的用法

iter()函数用于获取一个可迭代对象的迭代器next()函数用于获取迭代器的下一个元素

# 基本用法my_list=[10,20,30]it=iter(my_list)# 获取迭代器print(next(it))# 输出: 10print(next(it))# 输出: 20print(next(it))# 输出: 30print(next(it))# 抛出 StopIteration 异常

安全用法:使用for循环自动捕获StopIteration异常。

my_list=[10,20,30]foriteminmy_list:# for 循环底层自动调用了 iter() 和 next()print(item)

4. 自定义迭代器(面试常考)

通过定义一个类,实现__iter__()__next__()方法,就可以创建自定义迭代器。

classCountDown:"""自定义迭代器:从 n 倒数到 1"""def__init__(self,n):self.n=ndef__iter__(self):returnself# 迭代器返回自身def__next__(self):ifself.n<=0:raiseStopIteration# 必须抛出异常表示结束value=self.n self.n-=1returnvalue# 使用fornuminCountDown(5):print(num)# 输出: 5, 4, 3, 2, 1

三、生成器(Generator)—— 更优雅的迭代器

1. 什么是生成器?

生成器是一种特殊的迭代器,它使用yield关键字而不是return来返回值。

核心特点

  • 生成器用yield返回值,函数不会结束,而是暂停并保存当前状态
  • 下次调用next()时,从暂停处继续执行
  • 生成器是惰性求值的,需要时才产生数据

2. 创建生成器的两种方式

方式一:生成器函数(使用yield
defcount_down(n):"""生成器函数:从 n 倒数到 1"""whilen>0:yieldn# 返回 n,但函数暂停在这里n-=1# 调用生成器函数,返回一个生成器对象(不会立即执行)g=count_down(5)print(next(g))# 输出: 5print(next(g))# 输出: 4print(next(g))# 输出: 3print(next(g))# 输出: 2print(next(g))# 输出: 1# print(next(g)) # 抛出 StopIteration

关键理解:当函数执行到yield n时:

  1. n返回给调用方
  2. 函数暂停在这里,保存所有局部变量(n的值等)
  3. 下次调用next(g)时,从yield下一行继续执行
方式二:生成器表达式
# 列表推导式 vs 生成器表达式list_comp=[x*2forxinrange(5)]# 列表 → 立即计算,占用内存gen_exp=(x*2forxinrange(5))# 生成器 → 惰性计算,不占内存print(list_comp)# [0, 2, 4, 6, 8]print(gen_exp)# <generator object <genexpr> at 0x...>forvalueingen_exp:print(value)# 逐个输出: 0, 2, 4, 6, 8

3.yield的核心用法详解

yield是生成器最重要的关键字,它的核心作用可以用一句话概括:

yield让函数变成“可暂停、可恢复”的状态机。

基本用法:产生值
deffibonacci(n):"""生成斐波那契数列的前 n 项"""a,b=0,1count=0whilecount<n:yielda a,b=b,a+b count+=1fornuminfibonacci(10):print(num)# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
进阶用法:yield接收外部值(send()方法)

生成器不仅可以产出值,还可以接收外部传入的值:

defecho():"""生成器可以接收外部发送的值"""whileTrue:received=yield# 接收外部值print(f"收到:{received}")g=echo()next(g)# 预激活生成器,执行到 yieldg.send("Hello")# 发送值给生成器 → 输出: 收到: Hellog.send("World")# 发送值给生成器 → 输出: 收到: World

send()next()的区别

  • next(g):让生成器继续执行,直到遇到下一个yield
  • g.send(value):让生成器继续执行,并把value作为当前yield表达式的返回值
进阶用法:yield from(委托给子生成器)

yield from用于将迭代任务委托给另一个生成器,常用于扁平化嵌套结构:

defsub_generator():yield1yield2yield3defmain_generator():yield"开始"yieldfromsub_generator()# 委托给子生成器yield"结束"forvalueinmain_generator():print(value)# 输出: 开始, 1, 2, 3, 结束

实际应用:扁平化嵌套列表

defflatten(nested_list):"""将嵌套列表扁平化"""foriteminnested_list:ifisinstance(item,list):yieldfromflatten(item)# 递归委托else:yielditem nested=[1,[2,3],[4,[5,6]]]forvalueinflatten(nested):print(value)# 输出: 1, 2, 3, 4, 5, 6

四、迭代器 vs 生成器:一张表看懂区别

对比维度迭代器(Iterator)生成器(Generator)
实现方式定义类,实现__iter__()__next__()使用yield关键字的函数,或生成器表达式
代码量较多(需定义类)较少(函数简洁)
状态保存手动维护状态变量自动保存(挂起时保存所有局部变量)
惰性求值✅ 是✅ 是
内存占用极低极低
能否send()传值❌ 不能✅ 能
适用场景需要自定义复杂迭代逻辑大部分迭代场景,更简洁推荐

五、实战案例:什么时候用生成器?

案例1:读取超大文件(最常见场景)

defread_large_file(file_path):"""逐行读取超大文件,不一次性加载到内存"""withopen(file_path,'r',encoding='utf-8')asf:forlineinf:yieldline.strip()# 处理10GB的日志文件forlineinread_large_file("huge_log.txt"):if"ERROR"inline:print(line)# 逐行处理,内存安全

案例2:无限序列(你不知道什么时候结束)

definfinite_counter():"""无限计数器:从0开始一直数下去"""n=0whileTrue:yieldn n+=1counter=infinite_counter()print(next(counter))# 0print(next(counter))# 1print(next(counter))# 2# ... 可以无限取下去,不会内存溢出

案例3:流式处理数据管道

defread_data():"""读取原始数据"""foriinrange(10):yieldideffilter_even(data_stream):"""过滤出偶数"""forvalueindata_stream:ifvalue%2==0:yieldvaluedefsquare(data_stream):"""计算平方"""forvalueindata_stream:yieldvalue*value# 构建处理管道:读取 → 过滤偶数 → 求平方pipeline=square(filter_even(read_data()))forresultinpipeline:print(result)# 0, 4, 16, 36, 64

六、常见面试题汇总

Q1:returnyield有什么区别?

特性returnyield
函数状态结束,局部变量被销毁暂停,局部变量被保存
多次调用每次重新执行从上次暂停处继续执行
返回值返回一个值,函数结束返回一个值,函数暂停
内存效率一次性返回全部惰性生成,逐个返回

Q2:生成器是一次性的吗?

是的。生成器遍历完后不能重置,需要重新创建。

g=(xforxinrange(3))print(list(g))# [0, 1, 2]print(list(g))# [] → 已经消耗完了!

Q3:iter()next()的作用?

  • iter(obj):获取对象的迭代器(调用对象的__iter__()方法)
  • next(it):获取迭代器的下一个元素(调用迭代器的__next__()方法)

七、总结:一张图记住所有内容

可迭代对象 (Iterable) (实现了 __iter__) │ │ iter() ▼ ┌──────────────┐ │ 迭代器 (Iterator) │ │ __iter__ + __next__│ └──────────────┘ │ │ next() ▼ 逐个获取元素 (惰性求值) ┌───────────────────────────────────┐ │ 生成器 (Generator) │ │ 特殊的迭代器,用 yield 实现 │ │ 更简洁,自动保存状态 │ └───────────────────────────────────┘ │ ┌──────────────┴──────────────┐ │ │ 生成器函数 生成器表达式 (使用 yield) (使用小括号)

一句话核心记忆

  • 迭代器:实现了__iter____next__的“状态机”
  • 生成器:用yield写出来的迭代器,更简单、更优雅
  • iter():拿门票(获取迭代器)
  • next():往前走一步(获取下一个元素)
  • yield:暂停并返回,下次继续

八、写在最后

迭代器和生成器是Python中非常优雅的设计,它们让我们可以用极低的内存消耗处理海量数据。在接口自动化测试中,你可能用它来读取测试数据文件、处理分页接口的响应数据、实现流式断言等等。

掌握它们,你的Python水平就迈入了“进阶”的大门。

http://www.jsqmd.com/news/1153776/

相关文章:

  • 终极指南:5步掌握网易游戏NPK资源文件解包技术
  • 2026年AI智习室与普通智习室核心差异深度拆解:技术、实测与中立选型指南
  • 高危行业安全升级新赛道:智能穿戴+智慧平台构建动态风控体系
  • DMA详解:为什么USART、SPI、ADC都喜欢用DMA
  • STM32F042C6与ADS131M02高精度ADC系统设计指南
  • 如何用3步掌握AcFun视频下载:开源工具AcFunDown实用指南
  • Windows 12网页版:如何在浏览器中体验下一代操作系统界面?
  • 高端企业邮箱优选|Coremail云享版企业邮箱 助力高效办公
  • 解密本地化智能控制:Midea AC LAN如何实现美的设备局域网直连
  • 番茄小说下载器终极指南:如何轻松离线阅读全网小说
  • 五十种常见中药材识别检测数据集VOC+YOLO格式5085张50类别
  • 083、VSRT:视频超分中的Transformer架构——时空注意力机制详解
  • ADP5350与TM4C129EKCPDT电源管理方案解析
  • ArkTS 状态机实战:中式美食列表页怎么区分加载中、空结果和错误态
  • 如何让AI成为团队最靠谱的“超级同事”
  • 韩国主权AI战略:集中万块GPU加速自主大模型研发
  • ChatGPT API实战指南:从GPT-3.5到GPT-4o的集成、调优与深度排错
  • AI 大模型智能体应用软件定制开发公司——天磊卫士(深圳)科技有限公司
  • 3分钟学会Figma设计稿一键转代码:告别手动切图的终极指南
  • 膜结构汽车棚厂家哪家报价透明?
  • CCS + J-Link 调试立创地猛星 MSPM0G3507 无法烧录的一次恢复记录:“There is already a Code Composer debug session running“
  • MT5 科普:Python 如何辅助金融时间序列分析
  • 性价比高的书房书柜整木高定排行
  • Polar Code 编解码 MATLAB 实现
  • 第八次作业-用户管理和文件权限
  • PowerToys:Windows生产力的终极革命
  • 网盘直链下载助手:告别限速!九大网盘高速下载终极指南
  • AI编程实战指南:从工具选型到高效协作的完整工作流
  • 打造专属B站体验:Bilibili-Evolved模块化增强指南
  • 苹果电脑硬盘扩容到底有没有风险?一个华强北硬件工程师的实话