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

(生成器)yield与(迭代器)generator

一、问题起源:

def yield_test():a = iter(range(100))cnt = 1while True:cnt += 1if cnt > 100:breakprint('s')yield 1print('e')

输出无限循环

while True:print(next(yield_test()))

二、为什么:

外部的 while True 循环将会导致无限循环,但不是因为 yield_test() 函数内部的逻辑,而是因为外部循环每次迭代都重新创建了一个新的生成器对象。

  • yield_test() 被调用,返回一个新的生成器对象。
  • next(yield_test()) 调用新生成器的 next() 方法。
  • 生成器开始执行,直到遇到第一个 yield 语句,打印 ‘s’ 并产出值 1。
  • 生成器在 yield 处暂停执行,等待下一次 next() 调用以继续执行。
  • 因为外部循环没有保存生成器对象的引用,所以内部生成器的状态丢失了。
  • 外部循环重新开始,再次调用 yield_test() 创建一个新的生成器对象。
  • 步骤 2-6 重复进行,因此 ‘s’ 和 1 被无限打印出来,但 ‘e’ 永远不会被打印

可以忽略掉yield_test内部执行逻辑,从外部调用来看,while会一直执行yield_test方法,永远都不会有终止的时候。

三、修正方法:

a = yield_test()
while True:try:print(next(a))except StopIteration:# 当生成器耗尽,会抛出 StopIteration 异常break

why: iter(range(100))生成迭代器的本质是什么

Python 中,range 类型代表一个不变的数字序列,并且是惰性计算的,意味着它在需要时才计算每个值,而不是一开始就创建一个数字列表。range 对象可以通过 range(start, stop[, step]) 构造,其中 start 是序列的开始值,stop 是序列结束的边界值,而 step 是两个值之间的差距。

range 对象的 iter 方法负责返回一个迭代器,该迭代器会生成序列中的所有值。这个方法的实现是专门针对 range 对象优化的,并且是在 Python 的 C 语言层面实现的(如果你使用的是 CPython,即 Python 的一个官方实现)。这意味着 range 的 iter 方法是非常高效的,它不会创建一个包含所有值的列表,而是返回一个能够按需计算每个值的迭代器。

当你对 range 对象调用 iter() 函数时,或者在 for 循环中使用 range 对象时,iter 方法就会被调用。然而,因为这部分代码是在 Python 的底层实现中,我们通常看不到其具体的 Python 代码实现。但逻辑上,它大致等同于以下行为:

lass RangeIterator:def __init__(self, start, stop, step):self.current = startself.stop = stopself.step = stepdef __iter__(self):return selfdef __next__(self):if (self.step > 0 and self.current >= self.stop) or (self.step < 0 and self.current <= self.stop):raise StopIterationelse:value = self.currentself.current += self.stepreturn value

why: for循环迭代器时,为什么能自动遍历下一个元素呢

在 Python 中,for 循环是一种遍历迭代器的简便方法。它在内部自动处理迭代器的创建和迭代过程,包括调用迭代器的 next() 方法和处理 StopIteration 异常。这是 Python 语言的一个特性,旨在让迭代操作更加直观和易于使用。

当你使用 for 循环遍历一个可迭代对象时,Python 会执行以下步骤:

  • 调用可迭代对象的 iter() 方法来获取一个迭代器对象。
  • 在循环的每次迭代中,调用迭代器的 next() 方法来获取下一个元素。
  • 如果 next() 方法抛出 StopIteration 异常,这意味着迭代器已经没有更多元素可以遍历,for 循环会捕获这个异常并结束循环。
  • 在抛出 StopIteration 之前返回的每个元素都会被赋值给循环变量,并执行循环体。
    下面是一个简化的伪代码,展示了 for 循环背后的逻辑:
iterator = iter(iterable)  # 获取迭代器
while True:try:item = next(iterator)  # 获取下一个元素# 循环体代码,使用 itemexcept StopIteration:# 迭代器中没有更多元素break

why: 生成器和迭代器有什么区别

迭代器(Iterator)和生成器(Generator)是 Python 中实现迭代的两种对象,它们都遵循迭代器协议,但在使用和构建上有所不同。

迭代器(Iterator)

迭代器是一个遵循迭代器协议的对象,这意味着它支持两个方法:iter() 和 next()。iter() 方法返回迭代器对象本身,next() 方法返回序列中的下一个元素。如果所有元素都已经被返回,next() 应该抛出一个 StopIteration 异常。

迭代器可以从可迭代对象(例如列表、元组、集合、字典以及任何实现了 iter() 或 getitem() 方法的对象)中创建,通过调用 iter() 方法实现。

迭代器的关键特点是它们懒惰地计算值,也就是说,它们在请求下一个值之前不会计算该值,这允许它们表示非常大的或无限的序列。

生成器(Generator)

生成器是 Python 中构建迭代器的一种简单而强大的工具。任何包含 yield 表达式的函数都被称为生成器函数。当生成器函数被调用时,它返回一个生成器对象,而不是立即执行函数。

生成器对象本身也是迭代器,它支持上述的迭代器协议。当 next() 方法被调用时(通常是在 for 循环中),生成器函数将从上一次 yield 表达式暂停的地方开始执行,直到遇到下一个 yield 表达式。当生成器函数执行完成而没有遇到新的 yield 时,它将抛出 StopIteration 异常,表明迭代结束。

生成器的一个关键优势是它们可以非常节省内存,因为在任何给定时间都只生成序列中的一个元素。

区别总结
实现方式:迭代器通常由实现了迭代器协议的类的实例组成,而生成器则是由包含 yield 关键字的函数创建。
创建方法:迭代器是显式的,通常通过实现 iter() 和 next() 方法的类来创建。生成器是隐式的,更简单,仅需一个包含 yield 语句的函数。
状态保存:对于迭代器,状态的保存需要自己手动维护。而生成器函数在每次生成一个值后,状态会自动保存,当再次调用 next() 时,从上次返回的地方继续执行。
使用场景:生成器适合于简单的迭代场景,特别是当迭代逻辑可以通过单个函数表达时。迭代器适合于更复杂的情况,需要完全控制迭代逻辑时。

why:yield和return的本质区别是什么

yield 和 return 在 Python 中都用于从函数返回值,但它们在本质上有几个关键的区别:

函数类型

return: 使用 return 的函数是普通的函数。当一个函数执行到 return 语句时,它会返回指定的值,并且函数的执行完全结束。
yield: 包含 yield 语句的函数变成了一个生成器函数。这种函数当执行到 yield 语句时,会返回一个值,但函数的状态会被暂停并保存,以便后续从停止的地方继续执行。

执行流程

return: 只能在函数中出现一次(如果出现在循环或条件语句中,则可能执行多次),一旦执行,就标志着函数的结束。
yield: 可以出现多次,每次遇到 yield,函数会输出一个值并暂停执行,直到下一次通过迭代请求值时再继续执行。

返回值

return: 返回一个单一的值,这个值可以是任何数据类型(包括 None、单个值或者一个数据结构)。
yield: 每次返回迭代中的下一个值,对于生成器函数来说,每次 yield 会返回一个值给迭代器,而整个函数返回的是一个生成器对象。

内存消耗

return: 如果返回的是一个大型数据结构,如大列表,会占用相应的内存空间。
yield: 生成器一次只生成并返回一个值,因此不会一次性占用大量内存。这使得生成器特别适合大数据集或无限序列的情况。

用例

return: 当你需要立即获取一个完整的结果集时。
yield: 当你需要延迟计算结果,或者在计算大型或无限系列的数据集时以减少内存占用。

总的来说,yield 提供了一种生成值的惰性方法,使得函数可以在需要时才生成下一个值,而 return 提供了一种立即返回值的方法。这两种方法在不同的场景下各有优势。

why:yield的实现原理是什么

生成器函数

当你定义一个包含 yield 语句的函数时,这个函数成为生成器函数。与普通函数不同的是,当你调用生成器函数时,它并不立即执行,而是返回一个生成器对象。这个对象遵循迭代器协议,这意味着它实现了 iter() 和 next() 方法。

状态保存

生成器对象在内部保存了生成器函数的执行状态。这包括函数的局部变量、指令指针(即函数中的当前执行位置)和任何待处理的异常状态。这允许生成器函数在每次 yield 被调用时暂停其执行,并在下一次请求值时从上次离开的地方继续执行。

执行流程

当生成器的 next() 方法首次调用时,生成器函数开始执行,直到遇到第一个 yield 表达式。在这个时刻,函数暂停执行,并将 yield 后面的表达式的值返回给调用者。生成器函数的本地变量和执行状态被保存下来,等待下一次 next() 的调用。

当生成器的 next() 方法再次被调用时,生成器函数从上次 yield 暂停的地方恢复执行,直到遇到下一个 yield,或者函数执行结束。如果函数执行结束(即没有遇到新的 yield),生成器对象会抛出 StopIteration 异常,表明迭代已经完成。

内部实现机制

在更底层的实现上,Python 在编译包含 yield 的函数时,会将这个函数标记为生成器函数,并且为这个函数创建一个生成器对象。生成器对象包含一个执行帧(execution frame),用于保存执行状态。yield 本质上是产生一个值并暂停函数执行帧的指令。Python 运行时会处理这些执行帧的创建、保存和恢复。

应用

这种机制使得 yield 特别适合于创建可以生成大量值或无限序列的函数,因为它允许函数输出一个值后暂停执行,不需要一次性生成并保存所有值。

相关文章:

  • 从零开始学大模型 | 你必须要知道的三种大模型架构可视化的方法!
  • X进制减法(贪心算法C++实现)
  • Qt 图形视图 /图形视图框架坐标系统的设计理念和使用方法
  • 首个基于SSM-Transformer混合架构,开源商业大模型Jamba
  • 2022 Tesla AI Day -特斯拉自动驾驶FSD的进展和算法软件技术之数据以及虚拟
  • C++(8): std::deque的使用
  • Appium设备交互API
  • Hbase常用命令选择题
  • 【JavaSE】一维数组和二维数组详解
  • 小工具实战-Python实现小工具输出字符串大小写转换、字符串统计、编解码、MD5加密
  • Spring日志框架
  • 七、其它线性 DP
  • Git,GitHub,Gitee,GitLab 四者有什么区别?
  • 9.用FFmpeg测试H.264文件的解码时间
  • c入门基础题(2)
  • Docker下部署自己的LNMP工作环境
  • mysql中InnoDB引擎中页的概念
  • Phpstorm怎样批量删除空行?
  • Python_OOP
  • SQLServer之创建数据库快照
  • Vue小说阅读器(仿追书神器)
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 高程读书笔记 第六章 面向对象程序设计
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 事件委托的小应用
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • #每天一道面试题# 什么是MySQL的回表查询
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (JS基础)String 类型
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (新)网络工程师考点串讲与真题详解
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .a文件和.so文件
  • .NET 5.0正式发布,有什么功能特性(翻译)
  • .NET 解决重复提交问题
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • @ConfigurationProperties注解对数据的自动封装
  • []Telit UC864E 拨号上网
  • [C# WPF] 如何给控件添加边框(Border)?
  • [C#][opencvsharp]opencvsharp sift和surf特征点匹配
  • [C#]winform部署yolov5-onnx模型
  • [C\C++]读入优化【技巧】
  • [C++][基础]1_变量、常量和基本类型
  • [CISCN2019 华东北赛区]Web2
  • [CLickhouse] 学习小计
  • [C和指针].(美)Kenneth.A.Reek(ED2000.COM)pdf
  • [FC][常见Mapper IRQ研究]
  • [Mac软件]Boxy SVG 4.20.0 矢量图形编辑器
  • [NOIP2007 普及组] 纪念品分组--贪心算法
  • [python开发模拟netcat工具] BHPnet