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

Python高级编程之迭代器与生成器

一、先从一个简单问题开始什么叫“迭代”迭代就是“挨个儿取东西”。比如你有一排抽屉你从左到右一个个打开看这个过程就是迭代。Python里最常见的迭代就是for ... in ...for i in [1, 2, 3]: print(i)这个列表[1, 2, 3]就是一个可迭代对象Iterable因为它可以被for循环一个一个地取出元素。如何判断一个对象是否可迭代from collections.abc import Iterable print(isinstance([1,2,3], Iterable)) # True print(isinstance(abc, Iterable)) # True print(isinstance(123, Iterable)) # False一切可以被for循环遍历的东西基本都是可迭代对象列表、字符串、字典、文件、生成器等。二、揭秘for循环背后的男人迭代器2.1 什么又是迭代器Iterator迭代器是一个“记住了取到哪一步”的对象。它给你一个next()方法每次调用就返回下一个值直到没有元素时抛出StopIteration异常。你可以把迭代器想象成一个弹夹每按一下扳机next()就出一颗子弹打完了就报错。2.2 手动体验迭代器my_list [10, 20, 30] it iter(my_list) # 通过 iter() 把可迭代对象变成迭代器 print(next(it)) # 10 print(next(it)) # 20 print(next(it)) # 30 print(next(it)) # 抛出 StopIteration为什么需要迭代器因为不是所有可迭代对象都能一次加载到内存比如一个100GB的日志文件。用迭代器可以惰性求值——需要的时候才生成下一个值省内存。2.3 自己动手实现一个迭代器类要实现一个迭代器必须实现两个方法__iter__()返回自身或其它迭代器对象__next__()返回下一个值无值时抛StopIteration下面我们写一个斐波那契数列迭代器只存前两个数永远不占用大量内存class Fibonacci: def __init__(self, max_count): self.max max_count self.a 0 self.b 1 self.count 0 def __iter__(self): return self # 迭代器返回自身 def __next__(self): if self.count self.max: raise StopIteration self.count 1 value self.a self.a, self.b self.b, self.a self.b return value # 使用 fib Fibonacci(10) for num in fib: print(num, end ) # 0 1 1 2 3 5 8 13 21 34是不是觉得手写一个迭代器挺麻烦别急接下来更简单的生成器就登场了。三、生成器用yield让函数记住暂停点如果一个函数里出现了yield关键字它就不再是普通函数而是一个生成器函数。调用它不会立即执行而是返回一个生成器对象也属于迭代器。3.1 第一个生成器无限计数器def count_up_to(n): i 0 while i n: yield i # 每次返回 i并暂停在这里 i 1 gen count_up_to(3) print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 2执行过程第一次调用next(gen)→ 从函数开头运行遇到yield i返回0并冻结当前状态。第二次调用next(gen)→ 从上次暂停的地方继续i还是1然后yield返回1。直到循环结束函数自然返回时抛出StopIteration。3.2 生成器比列表省多少内存假设你想产生1亿个数# 列表方式 —— 内存爆炸 nums [x for x in range(100_000_000)] # 约800MB # 生成器方式 —— 几乎不占内存 def my_range(n): i 0 while i n: yield i i 1 gen my_range(100_000_000) # 只存一个整数 i用生成器不管要产生多少个数内存占用都是O(1)。3.3 生成器表达式简洁版生成器类似于列表推导式但把[]换成()# 列表推导式立刻生成全部数据 squares_list [x**2 for x in range(10)] # 生成器表达式惰性生成 squares_gen (x**2 for x in range(10)) print(type(squares_gen)) # class generator print(next(squares_gen)) # 0 print(next(squares_gen)) # 1何时用生成器表达式数据量大且只需要遍历一次时用生成器表达式代替列表推导式能大幅降低内存。四、进阶技巧yield from、send()、throw()、close()4.1yield from—— 委托给另一个生成器当你在一个生成器里想要产生另一个可迭代对象的所有值时不要写循环直接用yield fromdef chain(*iterables): for it in iterables: yield from it # 相当于 for x in it: yield x c chain([1,2,3], abc) print(list(c)) # [1, 2, 3, a, b, c]yield from还能自动传递send()和throw()在写复杂协程时特别有用。4.2send()—— 给生成器内部“喂”数据普通next()只能从生成器取数据send()不仅能取还能传进去一个值。def echo(): while True: received yield # yield 可以接收外部发送的值 print(f收到了: {received}) e echo() next(e) # 必须先启动到 yield 处 e.send(Hello) # 打印 收到了: Hello e.send(World) # 打印 收到了: World注意第一次必须先next(e)或e.send(None)让生成器运行到第一个yield位置。4.3throw()—— 在生成器内部抛出异常可以强制在生成器暂停的地方抛出一个异常def my_gen(): try: yield 1 yield 2 except ValueError: print(捕获到 ValueError) g my_gen() print(next(g)) # 1 g.throw(ValueError) # 打印 捕获到 ValueError4.4close()—— 提前终止生成器def infinite(): while True: yield 1 g infinite() print(next(g)) # 1 print(next(g)) # 1 g.close() print(next(g)) # 抛出 StopIteration不会再生成五、实战小例子用生成器读取超大日志文件假设你有一个10GB的日志文件app.log需要找出所有包含ERROR的行。用生成器可以一行一行读内存无忧def read_large_file(file_path): with open(file_path) as f: for line in f: yield line # 每次只返回一行 def filter_errors(lines): for line in lines: if ERROR in line: yield line # 链式调用全程惰性求值 errors filter_errors(read_large_file(app.log)) for err in errors: print(err.strip())你也可以用生成器表达式一步到位with open(app.log) as f: errors (line for line in f if ERROR in line) for err in errors: print(err.strip())六、总结记住这几点就够了概念一句话解释手写示例可迭代对象能用for循环的如list,str,file[1,2,3]迭代器实现了__next__()的对象可被next()调用iter([1,2,3])生成器函数含有yield的函数调用返回生成器对象def gen(): yield 1生成器表达式圆括号包裹的推导式(x*2 for x in range(5))yield from委托给另一个生成器或可迭代对象yield from abcsend()给生成器内部传递值gen.send(42)什么时候优先用生成器需要处理大数据流文件、数据库结果集、网络流需要无限序列如实时监控、轮询想要实现“管道式”的数据处理避免中间列表
http://www.zskr.cn/news/1409997.html

相关文章:

  • 研一开学别慌!用这套保姆级YOLOv5实战路线,从零到跑通代码只要三个月
  • 牛客R142(F树形DP)
  • 2026年 宝钢镀锌HC550/980DPD+Z双相钢厂家/供应商推荐榜:高强度与卓越成型性能的行业优选品牌 - 品牌企业推荐师(官方)
  • 第11章:AI辅助项目部署与运维——从测试网到主网
  • Tuna-2:直接像素embedding打败视觉编码器
  • 别再死记硬背公式了!用Python+Matplotlib手把手教你画滤波器的Bode图(附代码)
  • C# 终于支持 union types 了
  • .NET + 消息队列:稳稳扛住百亿流水,这才是企业级架构的真正底气
  • 从‘退化因子’到‘健康指标’:给你的机器人状态估计做个‘体检’
  • Python 入门:初识函数
  • 别再傻傻分不清!一文搞懂CPU、GPU、NPU、MCU、DSP、FPGA、SOC,嵌入式选型不踩坑
  • 侈品级不锈钢彩色板应用技术标准:从选材、工艺到验收的完整规范
  • DevOps CI/CD流水线最佳实践:从Git提交到生产部署的10分钟之旅
  • LVDS串行通信总线深度分析
  • 灰子学Ai: Token与字节
  • AI品牌命名避坑清单(含12个高危词根、6类语音陷阱、4种文化禁忌),错过本次更新将影响全球市场准入
  • SARscape版本升级实战:5.3到5.6.2,那些官方没细说的数据导入与DEM处理变化
  • 别再怕走廊和隧道了!用LOAM作者的方法搞定SLAM定位退化问题
  • 数据分析入门:手把手教你用Python爬取直播数据并做简单可视化
  • 开源语音AI的边界:从 `luongnv89/claude-howto` 看前沿技术的落地实践
  • 别再死记硬背公式了!用这个在线仿真工具,5分钟搞懂正激变换器(Forward Converter)工作原理
  • 2026指南:东莞老化房专业品牌厂家甄选 - 品牌企业推荐师(官方)
  • 算法:图的存储与遍历,最小生成树(Prim算法,kruskal算法)
  • 高光谱图像超分辨率技术:Mamba架构与实时处理实践
  • 别再只画轮廓了!用OpenCV的cv2.findContours()做点实际的:Python实现简易车牌识别
  • 别再破坏原车线束了!手把手教你用120通道BOB故障测试盒做汽车ECU信号诊断
  • 从野外数据到地下构造:手把手教你用地震时距曲线做一次‘虚拟勘探’
  • 别再死记硬背了!用“数据流”视角彻底理解F28335的SCI模块:从SCITXBUF到TXSHF发生了什么?
  • 告别ST-LINK!详解STM32G070RB开发板的串口一键下载配置与常见连接失败解决
  • 别再死记硬背了!用WideDeep模型搞定推荐系统里的‘记忆’与‘泛化’难题