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

python中的迭代器、可迭代对象和生成器

1. 前言

   迭代是一种遍历容器类型对象(例如字符串、列表、字典等等)的方式。例如,我们说迭代一个字符串“abc”,指的就是从左往右依次地、逐个地取出它的全部字符的过程(可以将迭代理解为遍历)。Python的迭代机制依赖于两个特殊方法:__iter____next____iter__方法返回一个迭代器对象,而__next__方法则负责返回迭代器的下一个值。当没有更多的值可返回时,__next__会抛出StopIteration异常。(这一段看不懂没关系!!!,继续往后看就明白了)
  我觉得可迭代对象(iterable)和迭代器(iterator)的概念有点抽象,容易把我搞懵,那怎么办呢。其实我们可以使用python的内置函数isinstance(obj, classinfo)来查询某个对象(obj)是否为指定的类型(classinfo),该函数的返回值为True或False。也就是说,我们可以使用内置函数isinstance(obj, classinfo)来判断该对象是否为可迭代对象或迭代器对象。代码示例如下(代码1):

# 代码1
from collections.abc import Iterable, Iterator
# 需要导入Iterable, Iterator
obj_list = [1,2,3,4]
obj_tuple = (1,2,3)
print(isinstance(obj_list, Iterable)) # 输出:True
print(isinstance(obj_list, Iterator)) # 输出:False
print(isinstance(obj_tuple, Iterable)) # 输出:True
print(isinstance(obj_tuple, Iterator)) # 输出:False

由上面代码可知:列表、元组都是可迭代对象(iterable),都不是迭代器对象(iterator)。下面介绍两个和迭代器相关的内置函数:iter()next()

(1)iter():该函数可以将可迭代对象(iterable)转换为迭代器(iterator),即iter()函数用于创建一个迭代器对象。iter()函数实际映射到类中的__iter__方法,即执行iter()函数会调用类中的__iter__方法。
(2)next():该函数可以返回迭代器(iterator)中的下一个元素,如果没有更多的元素,则引发StopIteration 异常。如果我们使用内置函数next()对迭代器进行遍历,在这个过程中,是在调用迭代器的__next__方法。即:next()函数内部调用迭代器的__next__方法。

from collections.abc import Iterable, Iteratormy_list = [1,2,3]
iter_list = iter(my_list)  # 将列表转换成迭代器对象
print(isinstance(iter_list, Iterable))  # 输出:True
print(isinstance(iter_list, Iterator))  # 输出:True
print(next(iter_list)) # 输出:1
print(next(iter_list)) # 输出:2
print(next(iter_list)) # 输出:3
print(next(iter_list)) # 输出:StopIteration

2. for循环遍历的原理

   大家对for循环不陌生吧,其实在for循环中就使用了iter()next()这两个内置函数。Python的for循环本质上就是通过不断调用迭代器的next()函数实现的,直到捕获 StopIteration 异常为止。在进行for循环遍历过程中就做两件事:(1)第一件:使用iter()函数将可迭代对象(iterable)转为迭代器对象(iterator);(2)第二件:反复对迭代器对象(iterator)使用next()函数,直到迭代器中的元素被获取完。最后捕获StopIteration异常,退出循环。对了,对于这个for循环语句for ... in ...,关键字in后面需要跟可迭代对象(iterable)。顺便再说一句:迭代器(iterator)一定是可迭代对象(iterable),即迭代器也是可迭代的

my_list = [1,2,3]
for i in my_list:print(i)# python 内部是这样转化的:
my_list = [1,2,3]
for i in iter(my_list ):  # 将列表(可迭代对象)转化迭代器(iterator)print(i)# for循环会自动处理这个StopIteration异常,以便结束for循环。

  我们也可以通过next()函数来遍历列表中的元素,代码示例如下(代码2)。由下面代码可知:next()函数依次取出迭代器中的元素,直到抛出StopIteration异常(如果不将列表(可迭代对象)通过iter()函数转换为迭代器对象,是没法使用next()函数来取出列表中的元素)。

# 代码2
my_list = [1,2,3]
iter_list = iter(my_list)  # 将列表转换成迭代器对象
print(next(iter_list)) # 输出:1
print(next(iter_list)) # 输出:2
print(next(iter_list)) # 输出:3
print(next(iter_list)) # 输出:StopIteration

3. 可迭代对象(iterable)

   如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象。常见的可迭代对象有:列表(list)、元组(tuple)、字典(dict)和文件对象等等。下面代码中(代码3),在MyList类中实现了__iter__方法,所以对象obj_list是可迭代对象(iterable),但对象obj_list不是迭代器(iterator)。虽然对象obj_list是可迭代对象(iterable),但它却不能被迭代(遍历)。

# 代码3
from collections.abc import Iterable, Iteratorclass MyList:def __init__(self,*args) -> None:self.data = list(args)def __iter__(self):print("__iter__被执行了...")return selfobj_list = MyList(2,4,6)
print(isinstance(obj_list, Iterable))  # 输出:True
print(isinstance(obj_list, Iterator))  # 输出:False

4. 迭代器(iterator)

  在Python中,如果一个对象同时实现了__iter__方法和__next__方法,那它就是迭代器。也就是说,迭代器是一个实现了__iter____next__方法的对象。__iter__方法返回迭代器对象自身,而 __next__方法返回下一个元素。换句话说,迭代器是一个可以逐个返回元素的对象。如果在python的类中定义了__next____iter__方法,生成的实例对象可以通过for循环遍历来取,并且先调用__iter__方法,再调用__next__方法。示例代码如下所示(代码4):

# 代码4
from collections.abc import Iterable, Iteratorclass MyList:def __init__(self,*args) -> None:self.data = list(args)def __iter__(self):print("__iter__被执行了...")return selfdef __next__(self):pass
obj_list = MyList(2,4,6)
print(isinstance(obj_list, Iterable))  # 输出:True
print(isinstance(obj_list, Iterator))  # 输出:True

由于我们在类中实现了__iter____next__方法,所以对象obj_list既是可迭代对象(iterable),又是一个迭代器(iterator)。由此可得出一个结论:迭代器(iterator)一定是可迭代对象(iterable)

5. 自定义迭代器

代码如下所示(代码5):

from collections.abc import Iterable,Iteratorclass MyList:def __init__(self,*args) -> None:self.data = list(args)self.start = 0def __iter__(self):print("__iter__被执行了...")return selfdef __next__(self):print("__next__被执行了...")if self.start >= len(self.data):# raise StopIteration用于提前终止一个迭代器中的循环raise StopIterationitem = self.data[self.start]self.start += 1return itemobj_list = MyList(2,4,6)
print(isinstance(obj_list,Iterable)) # 输出:True
print(isinstance(obj_list,Iterator)) # 输出:Truefor i in obj_list:print(i)输出:
True
True
__iter__被执行了...
__next__被执行了...
2
__next__被执行了...
4
__next__被执行了...
6
__next__被执行了...

小结

  (1)迭代器(iterator): 如果一个对象同时实现了__iter__方法和__next__方法,那它就是迭代器;

  (2)可迭代对象(iterable): 如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象。通常__iter__方法需要返回一个实现了__next__方法的对象,如果自己实现了,可以返回self,当然这个返回值不是必须的。

  (3)两者之间的关系:迭代器(iterator)一定是可迭代对象(iterable),反之则不成立。可迭代对象的__iter__方法必须返回一个迭代器。

参考文章:一文看懂python的迭代器和可迭代对象

6. 生成器(generator)

  在 Python 中,使用了关键字yield的函数被称为生成器。生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,简单点说生成器就是一个迭代器
  生成器的关键在于yield语句。yield语句的作用和return语句有几分相似,都可以将结果返回。不同在于,生成器函数执行到yield语句,返回结果的同时记录下函数内的状态,下次执行这个生成器函数,将从上次退出的位置(yield的下一句代码)继续执行。当生成器函数中的所有代码被执行完毕时,自动抛出 StopIteration异常。
  (1)生成器函数:生成器的用法和迭代器相似,都可以使用 next()函数来进行迭代。这是因为生成器其实就是创建迭代器的便捷方法,生产器会在背后自动定义 __iter__ __next__方法。

# 生成器函数
def  my_generator():for i in range(4):yield ia = my_generator()
print(a)  # 输出:<generator object gen at 0x0000029481B899C8>
print(type(a)) # 输出:<class 'generator'>
print(next(a)) # 输出:0
print(next(a)) # 输出:1
print(next(a)) # 输出:2
print(next(a)) # 输出:3
print(next(a)) # 输出:StopIteration

  (2)生成器表达式:生成器 = (关于item的表达式 for item in 可迭代对象)

my_generator = (item*2 for item in [2,4,6])
print(type(my_generator)) # 输出:<class 'generator'># 使用for循环遍历生成器
for i in my_generator:print(i) # 输出:4,8,12print(next(my_generator)) # 输出:4
print(next(my_generator)) # 输出:8
print(next(my_generator)) # 输出:12
print(next(my_generator)) # 输出:StopIteration

参考文章:Python进阶干货速递!【超详细迭代器、生成器、装饰器使用教程】

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 时序预测|基于变分模态分解-时域卷积-双向长短期记忆-注意力机制多变量时间序列预测VMD-TCN-BiLSTM-Attention
  • 【Linux】网络编程套接字Scoket:UDP网络编程
  • 如何设置RabbitMQ和Redis消息队列系统
  • 目标检测——YOLOv8训练自己的数据集
  • 一些常见的数据处理技术
  • android compose contraintlayout 使用 bias
  • 初识C++ · C++11(1)
  • 代码随想录——判断子序列(Leetcode 392)
  • 立仪科技光谱共焦应用之金属隔膜静态重复性测量
  • 化工材料分析丨结构分析丨配方分析丨元素分析
  • 第一百八十八节 Java XML教程 - Java StAX
  • 前端 package.json 的每一项作用
  • 初始化列表的基本介绍
  • 数学建模~~追逐仿真问题
  • 无人机培训机构推广运营理论技术
  • axios 和 cookie 的那些事
  • Bytom交易说明(账户管理模式)
  • canvas 绘制双线技巧
  • HashMap剖析之内部结构
  • javascript从右向左截取指定位数字符的3种方法
  • java概述
  • Leetcode 27 Remove Element
  • MD5加密原理解析及OC版原理实现
  • npx命令介绍
  • Terraform入门 - 3. 变更基础设施
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 大整数乘法-表格法
  • 力扣(LeetCode)22
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 面试总结JavaScript篇
  • 数据科学 第 3 章 11 字符串处理
  • 我的业余项目总结
  • MiKTeX could not find the script engine ‘perl.exe‘ which is required to execute ‘latexmk‘.
  • 仓管云——企业云erp功能有哪些?
  • 如何用纯 CSS 创作一个货车 loader
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (android 地图实战开发)3 在地图上显示当前位置和自定义银行位置
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (Git) gitignore基础使用
  • (ros//EnvironmentVariables)ros环境变量
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (一)十分简易快速 自己训练样本 opencv级联haar分类器 车牌识别
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (转)关于pipe()的详细解析
  • .a文件和.so文件
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .Net 8.0 新的变化