Python 可迭代对象(Iterable)、迭代器(Iterator)与生成器(generator)之间的相互关系

news2024/12/26 2:38:01

1、迭代

通过重复执行的代码处理相似的数据集的过程,并且本次迭代的处理数据要依赖上一次的结果继续往下做,上一次产生的结果为下一次产生结果的初始状态,如果中途有任何停顿,都不能算是迭代。

# 非迭代例子
n = 0
while n < 3:
    print('hello world')
    n += 1
hello world
hello world
hello world
# 迭代例子
n = 0
while n < 3:
    print('第%s次 hello world'%(n+1))
    n += 1
第1次 hello world
第2次 hello world
第3次 hello world

前者循环内3次输出" Hello world",输出的数据不依赖上一次的数据,因此不是跌代。而后者的循环内打印的3次数据都上一次循环的影响,所以是迭代。

2、容器 container

容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用 in, not in关键字判断元素是否包含在容器中。

  • 从字面上理解,容器基本上可以包含其他类型对象(如列表、元组、字典等)作为元素的对象;在 Python 常见的数据类型中几乎所有的数据类型(字符串除外)都能包含其它类型的对象;

  • 容器仅仅只是用来存放数据的,我们平常看到的 l = [1,2,3,4] 等等,表面上我们可以直接从列表容器中取出元素,但事实上容器并不提供这种能力,而是可迭代对象赋予了容器这种能力。

3、可迭代对象(Iterable)

3.1 基本概念

可迭代对象并不是指某种具体的数据类型,它是指存储了元素的一个容器对象,且容器中的元素可以通过__iter__( )方法或__getitem__( )方法访问。

  • Python实现一个通用的外部可以访问可迭代对象内部数据的接口:

    • __iter__()方法的作用是让对象可以用 for ... in 循环遍历
    • __getitem__( )方法是让对象可以通过“实例名[index/key]”的方式访问实例中的元素
  • 一个可迭代对象是不能独立进行迭代的,Python中,迭代是通过 for ... in 来完成的。凡是可迭代对象都可以直接用 for...in 循环访问,这个语句其实做了两件事:第一件事是调用 __iter__() 获得一个可迭代器,第二件事是循环调用__next__()

3.2 判断一个对象是不是可迭代对象

可以使用 isinstance() 判断一个对象是否是 Iterable 对象

from collections import Iterable

class MyDataType1:
    # 自定义的数据类型1
    def __iter__(self):
        pass
    
class MyDataType2:
    # 自定义的数据类型2
    pass
    
# 自定义的数据类型实例化
mydatatype1 = MyDataType1()
mydatatype2 = MyDataType2()

data_info = {"字典":{},  "列表":[],  "字符串":'abc',
             "元祖":(1,),"集合":{1,},"MyDataType1": mydatatype1, "MyDataType2": mydatatype2, 
             "数字":100,"boolen":True, "None":None}
for key,value in data_info.items():
    result = isinstance(value, Iterable)
    if result:
        print(f"{key} is Iterable")
    else:
        print(f"{key} is not Iterable")

字典 is Iterable
列表 is Iterable
字符串 is Iterable
元祖 is Iterable
集合 is Iterable
MyDataType1 is Iterable
MyDataType2 is not Iterable
数字 is not Iterable
boolen is not Iterable
None is not Iterable

结合上述代码发现,添加了__iter__方法的 MyDataType1 对象已经是一个可迭代对象啦!!!

3.3 __iter__()函数与next()函数

  • iter 函数是让对象可以用 for ... in 循环遍历,即将对象转变为迭代器。
  • next 函数要和iter() 函数一起使用,不断使用next()函数来获取下一条数据(for 循环实现的原因)。
# 没有 iter 时,next 能使用
li = [11, 22, 33, 44, 55]
next(li)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-4-b5d17e146774> in <module>
      1 # 没有 iter 时,next 能使用
      2 li = [11, 22, 33, 44, 55]
----> 3 next(li)


TypeError: 'list' object is not an iterator
li = [11, 22, 33, 44, 55]
li = iter(li) # iter()函数实际上就是调用了可迭 代对象的 __iter__ 方法。
next(li)
11

4、迭代器(Iterator)

4.1 基本概念

迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素,从实现上来看,一个迭代器对象必须是定义了__iter__()方法和next()方法的对象。

  • Python 的 Iterator对象 表示的是一个数据流,可以把这个数据流看做是一个有序序列,但不能提前知道序列的长度,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算;
  • Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误;
  • 所有的 Iterable 可迭代对象均可以通过内置函数 iter()来转变为迭代器 Iterator。
  • 优点:节约内存(循环过程中,数据不用一次读入,在处理文件对象时特别有用,因为文件也是迭代器对象)、不依赖索引取值、实现惰性计算(需要时再取值计算);
  • 缺点: 迭代器使用上存在限制:只能向前一个个地访问数据,已访问数据无法再次访问、遍历访问一次后再访问无数据
# 不能提前知道迭代器的长度
li = iter([11, 22]) 
print(len(li))
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-6-0f3ba65b9307> in <module>
      1 # 不能提前知道迭代器的长度
      2 li = iter([11, 22])
----> 3 print(len(li))


TypeError: object of type 'list_iterator' has no len()
# next 访问直至出现 StopIteration
li = iter([11, 22]) 
print(next(li))
print(next(li))
print(next(li))
11
22
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-7-3d729d10b8b7> in <module>
      3 print(next(li))
      4 print(next(li))
----> 5 print(next(li))

StopIteration: 
# for 循环访问
li = iter([11, 22])
for i in li: print(i)
11
22

4.2 如何判断一个对象是否是迭代器

可以使用 isinstance() 判断一个对象是否是 Iterator 对象

from collections import Iterator

for key,value in data_info.items():
    result = isinstance(value, Iterable)
    if result:
        print(f"{key} is Iterable  ", end=' ')
    else:
        print(f"{key} is not Iterable  ", end='')
    if isinstance(value, Iterator):
        print(f"is Iterator")
    else:
        print(f"is not Iterator")
字典 is Iterable   is not Iterator
列表 is Iterable   is not Iterator
字符串 is Iterable   is not Iterator
元祖 is Iterable   is not Iterator
集合 is Iterable   is not Iterator
MyDataType1 is Iterable   is not Iterator
MyDataType2 is not Iterable  is not Iterator
数字 is not Iterable  is not Iterator
boolen is not Iterable  is not Iterator
None is not Iterable  is not Iterator
class MyIterator:
    def __iter__(self):
        pass
    def __next__(self):
        pass
myiterator = MyIterator()
print(isinstance(myiterator, Iterable)) 
print(isinstance(myiterator, Iterator))
True
True

我们发现:

  • 一个实现了 __iter__() 方法和 __next__() 方法的对象,才是迭代器。
  • 一个对象是可迭代对象,但是不一定是迭代器;一个迭代器对象一定是可迭代对象。

5、生成器 (generator)

在 Python 中,使用了 yield 的函数被称为生成器。

跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用一个生成器函数,返回的是一个迭代器对象。

5.1 创建生成器的方式1:

# 第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
L = [ x*2 for x in range(5)]
print(L) 

G = ( x*2 for x in range(5))
print(G)
[0, 2, 4, 6, 8]
<generator object <genexpr> at 0x10c02fb48>

创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表,而 G 是一个生成器。

我们可以直接 打印出列表L的每一个元素,而对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通 过next()函数、for循环、list()等方法使用。

5.2 创建生成器的方式2:

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生
f
<generator object fibonacci at 0x10c02f9e8>

5.3 访问生成器数据

# list 等方法
print("第1种方式访问:")
print(list(fibonacci(10)))
    
# next 函数
print("第2种方式访问:")
f = fibonacci(10)
while True:
    try:
        print(next(f), end=" ")
    except StopIteration:
        print()
        break
    
# for 循环
print("第3种方式访问:")
f = fibonacci(10)
for i in f:
      print(f"{i} ", end="")
第1种方式访问:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
第2种方式访问:
0 1 1 2 3 5 8 13 21 34 55 
第3种方式访问:
0 1 1 2 3 5 8 13 21 34 55 
  • 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
  • yield关键字有两点作用:
    • 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
    • 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
  • 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)

5.4 生成器与生成器函数的判断区分

使用 isgeneratorfunction 判断一个函数是不是生成器函数;
使用 isinstance 和 types.GeneratorType 判断一个对象是不是生成器对象

from inspect import isgeneratorfunction 
import types

def f(x):
    # 生成器函数
    yield x
    
# 生成器对象
G = (x*2 for x in range(5))

# 生成器函数的判断
print("f 函数是生成器函数?:", isgeneratorfunction(f))
print("f(5) 是生成器函数?:", isgeneratorfunction(f(5)))

# 生成器对象的判断
print("f(5) 是生成器对象?:", isinstance(f(5), types.GeneratorType))
print(" G   是生成器对象?:", isinstance(G, types.GeneratorType))
f 函数是生成器函数?: True
f(5) 是生成器函数?: False
f(5) 是生成器对象?: True
 G   是生成器对象?: True

6、最后用一张图来表达他们之间的关系

jupyter

参考资料:

  • https://blog.csdn.net/LaoYuanPython/article/details/89609187
  • https://www.cnblogs.com/Aiyuqianer/p/14091062.html
  • https://www.runoob.com/python3/python3-iterator-generator.html
  • https://zhuanlan.zhihu.com/p/341439647

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/78146.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SSM如何

目录 1、整合Mybatis 1.1.新建项目 1.2.添加pom依赖 1.3.application.yml 1.4.generatorConfig.xml 1.5.设置逆向生成 1.6.编写controller层 1.7.测试 2、整合 Mybatis-plus 2.1Mybatis-plus简介 2.2.创建项目 2.3.添加pom依赖 2.4.application.yml 2.5.MPGenerator 2.6.生成…

Stm32旧版库函数1——adxl335 模拟输出量 usart2

主函数&#xff1a; /******************************************************************************* // // 使用单片机STM32F103C8T6 // 晶振&#xff1a;8.00M // 编译环境 Keil uVision4 // 在3.3V的供电环境下&#xff0c;就能运行 // 波特率 19200 串口2 PA2(Tx) P…

equals方法:黑马版

目录 Object类的equals方法 Student类 测试类 第一步&#xff1a;使用比较 第二步&#xff1a;使用equals比较 第三步&#xff1a;在子类-Student类中重写equals方法 代码逐句分析 运行 Object类的equals方法 首先写一个类Student&#xff0c;属性有name和age&#xf…

UE5笔记【十二】蓝图函数BluePrint Function

上一篇讲了蓝图变量&#xff0c;这一篇说蓝图函数。BluePrint Function 函数&#xff0c;一般是为了将一段功能的代码提取出来&#xff0c;然后方便我们反复使用。重复的代码可以提取一个函数。类似的&#xff0c;相同的蓝图&#xff0c;我们也可以提取出一个蓝图函数来。 如…

青龙面板 香蕉

香蕉角本教程 介绍 香蕉视频 app —【多用户版】 一个账户每天稳定1元&#xff0c;可以自己提现&#xff0c;也可以兑换会员&#xff0c;脚本不停会员也不停&#xff01;可注册多个账户&#xff01;&#xff08;多账户福利自行看文章底部&#xff01;&#xff09; 拉取文件 …

【微服务】springboot 整合javassist详解

一、前言 Javassist 是一个开源&#xff0c;用于分析、编辑和创建Java字节码的类库&#xff0c;由东京工业大学数学和计算机科学系的 Shigeru Chiba &#xff08;千叶滋&#xff09;所创建。目前已加入了开放源代码JBoss 应用服务器项目&#xff0c;通过使用Javassist对字节码操…

linux redhat 8 创建逻辑卷

LVM与直接使用物理存储相比,有以下优点: 1. 灵活的容量. 当使用逻辑卷时,文件系统可以扩展到多个磁盘上,你可以聚合多个磁盘或磁盘分区成单一的逻辑卷. 2. 方便的设备命名 逻辑卷可以按你觉得方便的方式来起任何名称. 3.磁盘条块化. 你可以生成一个逻辑盘,它的数据可以被…

记录一次Mac本地启动nacos遇到的问题

nacos 官网&#xff1a;https://nacos.io/zh-cn/docs/quick-start.html 我这里下载的是2.0.3稳定的版本 本地启动&#xff1a;sh startup.sh -m standalone 问题1&#xff1a;Caused by: java.lang.IllegalStateException: No DataSource set 这里是数据源连接有问题&#xff…

Linux网络原理及编程(6)——第十六节 TCP可靠性保证的原理

目录 1、确认应答机制 2、超时重传机制 3、滑动窗口 4、流量控制 5、拥塞控制 6、延迟应答 &#xff08;各位好&#xff0c;博主新建了个公众号《自学编程村》&#xff0c;拉到底部即可看到&#xff0c;有情趣可以关注看看哈哈&#xff0c;关注后还可以加博主wx呦~~~&am…

Apache Flink 水印的工作机制详解与源码阅读

一、时间长河谁能解 在人类生存的地球上&#xff0c;存在着一种很神秘的东西&#xff1a;时间&#xff0c;它看不见摸不着&#xff0c;但速度恒定&#xff0c;单调递增且永无止境的往前推进&#xff0c;人类的历史被淹没在茫茫的时间长河中。同时在地球附近&#xff0c;一个星…

【自动化测试】如何平衡手工和自动化测试

作为一名测试人员&#xff0c;如何平衡手工和自动化测试&#xff0c;是一道绕不过去的课题。不可否认&#xff0c;自动化测试具有提高效率&#xff0c;加快回归速度并因此有助于及时交付项目的好处。但是&#xff0c;在考虑自动化之前&#xff0c;我们应该评估一些要点&#xf…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java毕业生就业管理系统243xa

首先选择计算机题目的时候先看定什么主题&#xff0c;一般的话都选择当年最热门的话题进行组题&#xff0c;就比如说&#xff0c;今年的热门话题有奥运会&#xff0c;全运会&#xff0c;残运会&#xff0c;或者疫情相关的&#xff0c;这些都是热门话题&#xff0c;所以你就可以…

vscode 关闭/忽略/ignore 单个/指定 git 仓库/repository 提示

文章目录1. 问题2. 解决方法2.1 只追踪 打开文件 所在的仓库2.2 忽略指定的仓库3. 参考1. 问题 vscode 当打开的项目中有多个 git 仓库时&#xff0c;默认会显示所有仓库的 status。 有些已经不再使用的仓库可能有多处更改&#xff0c;但我们并不想去处理它。 如果直接关闭 vs…

gcc 4.8.5 的string问题

gcc 4.8.5及一下版本 的string 因为内部内存管理采用了COW的方式&#xff0c;导致了string在多线程的状态下容易崩溃。崩的很没有规律&#xff0c;表现就是string在赋值的时候就莫名其妙的崩了&#xff0c;查看堆栈&#xff0c;你也看不出啥。崩溃的很随机。 为了避免崩溃&…

文本纠错--文本分割N-gram--Macbert模型的调用以及对返回结果的处理

文本根据词典进行纠错 输入一段可能带有错误信息的文字&#xff0c; 通过词典来检测其中可能错误的词。 例如&#xff1a;有句子如下&#xff1a;中央人民政府驻澳门特别行政区联络办公室1日在机关大楼设灵堂    有词典如下&#xff1a;中国人民&#xff0c;中央人民&#x…

漏斗分析 - AARRR模型案例分析

漏斗分析是一套流程式的数据分析方法&#xff0c;能够科学地反映各阶段用户转化情况。漏斗分析模型已经广泛应用于用户行为分析类产品&#xff0c;且功能十分强大&#xff1a;它可以评估总体或各个环节的转化情况、促销活动效果&#xff1b;也可以与其他数据分析模型结合进行深…

【三维目标检测】SSN(二)

SSN数据和源码配置调试过程请参考上一篇博文&#xff1a;【三维目标检测】SSN&#xff08;一&#xff09;_Coding的叶子的博客-CSDN博客。本文主要详细介绍SSN网络结构及其运行中间状态。 1 模型总体过程 SSN主要结构如下图所示&#xff0c;其核心在于提出了shape-aware heads…

认识分布式锁、使用分布式锁 Redission、实现秒杀案例

分布式锁 基本原理 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程进行&#xff0c;让程序串行执行&…

[附源码]计算机毕业设计基于Vue的社区拼购商城Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

node.js-fs模块学习

目录 1.使用fs.readFile方法读取文件 2.使用fs.writeFile方法写入文件 3.fs小案例-整理成绩并写入到新文件中 4.fs模块-路径动态拼接的问题 1.使用fs.readFile方法读取文件 //导入fs模块 const fs require(fs)//调用fs读取文件 //参数1&#xff1a;读取文件的存放路径 //…