Python迭代器使用详解

作者:酱油哥,清华大学计算机硕士,泰康资管/军工央企职场经历。


0.本集概览

  1. 可迭代对象、迭代器、迭代协议究竟是什么

  2. 迭代过程手动演示

  3. 典型可迭代对象举例:文件、字典以及range、enumerate返回值

  4. 使用迭代协议的逐项扫描工具可以称之为迭代环境

  5. 迭代环境还包含很多可以传入可迭代对象的内置方法

这一集的内容看起来比较绕,反反复复出现的是迭代二字。大家注意,这一节的内容很pythonic,是很有特色也非常重要的知识点。敲黑板啦!

1.可迭代对象

Python中有一类工具叫做迭代工具,他们能从左至右扫描对象。这包括了for循环、列表解析、in成员关系测试以及map内置函数等。而可迭代对象,顾名思义就是可以用在上述迭代工具环境中,通过一次次迭代不断产生结果的对象

可迭代对象分为两大类,一种是实际保存的序列,即列表、元组,字符串;另一种就是我们上一节总提到的“不一次性产生所有结果列表,而是可以在for循环中按需一次产生一个结果的对象”。如:range函数返回值、zip函数返回值、enumerate函数返回值等等,与实际序列相对应,这个叫做按需产生对象的虚拟序列。

2.迭代器

我们注意到,这一小节的标题中还有一个词叫做迭代器,他俩放在一起容易把人弄迷糊,他们之间的关系,相信你看完下面一段介绍就会很清楚了:

2.1.迭代器与可迭代对象的关系

可迭代对象支持内置函数iter,通过对可迭代对象调用iter函数,会返回一个迭代器,而“迭代器”支持内置函数next,通过不断对其调用next方法,会依次前进到序列中的下一个元素并将其返回,最后到达一系列结果的末尾时,会引发StopIteration异常。补充说明一点,对迭代器调用iter方法,则会返回迭代器自身。 代码片段:

 
 
  1. L = [2,3,4]

  2. I = iter(L)

  3. print(iter(L))

  4. print(I is L)

  5. print(I is iter(I))

运行结果:

 
 
  1. <list_iterator object at 0x00000000021E0978>

  2. False

  3. True

从这个例子中我们看到,通过iter函数将迭代对象转化成迭代器,而对迭代器调用iter函数,依然返回迭代器。

2.2.迭代过程演示

我们来完整的看看迭代过程是怎么实现的:当任何可迭代对象传入到for循环或其他迭代工具中进行遍历时,迭代工具都是先通过iter函数获得与可迭代对象对应的迭代器,然后再对迭代器调用next函数,不断的依次获取元素,并在捕捉到StopIteration异常时确定完成迭代,这就是完整的迭代过程。这也称之为“迭代协议”。

2.2.1.手动演示迭代协议

还是举例子说明,我们来模拟for循环如何处理内置可迭代对象-----内置类型列表L: 代码片段:

 
 
  1. L = [2,3,4]

  2. I = iter(L)

  3. print(next(I))

  4. print(next(I))

  5. print(next(I))

  6. print(next(I))

运行结果:

 
 
  1. 2

  2. 3

  3. 4

  4. Traceback (most recent call last):

  5. File "E:/12homework/12homework.py", line 6, in <module>

  6. print(next(I))

  7. StopIteration

在这个手动模拟迭代的过程中,先把可迭代对象转换成迭代器,然后用next方法进行手动迭代,迭代到最后出现StopIteration异常退出

2.2.2.实际的自动迭代过程

与手动迭代的示例过程相对应,下面是用for循环进行自动迭代的过程,这个大家都很熟悉。 代码片段:

 
 
  1. L = [2,3,4]

  2. for x in L:

  3.    print(x)

运行结果:

 
 
  1. 2

  2. 3

  3. 4

2.3.典型对象迭代举例

2.3.1.迭代文件对象

open函数返回的已打开的文件对象,也是可以一行一行的读取,直至文件结束,那很显然,他也是可迭代对象。代码片段:

 
 
  1. f = open('myfile.txt')

  2. print(f is iter(f))

运行结果:

 
 
  1. True

从这个例子中我们可以看出,文件对象的迭代器就是他自己。即文件对象既是迭代器,又是可迭代对象。我们还是用上面的手动模拟迭代的方式来一探究竟: 代码片段:

 
 
  1. f = open('myfile.txt')

  2. print(next(f))

  3. print(next(f))

  4. print(next(f))

  5. print(next(f))

运行结果:

 
 
  1. hello text file

  2. goodbyt text file

  3. hahahahah

  4. Traceback (most recent call last):

  5. File "E:/12homework/12homework.py", line 5, in <module>

  6. print(next(f))

  7. StopIteration

对应的,直接用for循环来进行自动迭代: 代码片段:

 
 
  1. f = open('myfile.txt')

  2. for line in f:

  3.    print(line, end='')

运行结果:

 
 
  1. hello text file

  2. goodbyt text file

  3. Hahahahah

之前我们提到过,这是读取文件的最佳方式,首先是简单、运行速度快,并且从内存使用情况而言也是最好的。让我们对比一下较为原始的readlines方法: 代码片段:

 
 
  1. f = open('myfile.txt')

  2. for line in f.readlines():

  3.    print(line, end='')

运行结果:

 
 
  1. hello text file

  2. goodbyt text file

  3. Hahahahah

对比来看,虽然readlines方法在功能上可用,但从内存上来看,非常糟糕,他是一次性把整个文件加载到内存,如果文件太大,以至于计算机内存不够,甚至不能够工作。而我们的迭代器版本则不然,迭代器是按需,一次只读取一行,因此对内存爆炸问题有了很好的免疫。

2.3.2.迭代字典对象

文件和列表对象都是实际的序列,他所迭代的就是他的实际内容,那字典呢?字典也是一种可迭代对象,但是他的迭代器却比较特殊。我们看看手动模拟迭代过程的例子: 代码片段:

 
 
  1. D = {'a':1, 'b':2, 'c':3}

  2. I = iter(D)

  3. print(next(I))

  4. print(next(I))

  5. print(next(I))

  6. print(next(I))

运行结果:

 
 
  1. a

  2. b

  3. c

  4. Traceback (most recent call last):

  5. File "E:/12homework/12homework.py", line 6, in <module>

  6. print(next(I))

  7. StopIteration

同样,在for循环中自动迭代的例子如下: 代码片段:

 
 
  1. D = {'a':1, 'b':2, 'c':3}

  2. for k in D:

  3.    print(k)

运行结果:

 
 
  1. c

  2. a

  3. b

因此不难看出,字典也是一个可迭代对象,字典有一个迭代器,在迭代环境中,会每次自动地返回一个键

而需要补充的是,字典拥有不同视图的可迭代对象,这里就不详细一一展开了,看看几个例子,分别是各自不同视图下的可迭代对象和迭代器,他们也是一次产生一个结果项,而不是在内存中一次产生全部结果列表。 代码片段:

 
 
  1. D = {'a':1,'b':2,'c':3}

  2. print(D.keys())

  3. print(iter(D.keys()))

  4. print(D.values())

  5. print(iter(D.values()))

  6. print(D.items())

  7. print(iter(D.items()))

运行结果:

 
 
  1. dict_keys(['b', 'a', 'c'])

  2. <dict_keyiterator object at 0x0000000002202958>

  3. dict_values([2, 1, 3])

  4. <dict_valueiterator object at 0x0000000002202958>

  5. dict_items([('b', 2), ('a', 1), ('c', 3)])

  6. <dict_itemiterator object at 0x00000000022029A8>

再举两个我们上一节提到的例子:range函数和enumerate函数

2.3.3.迭代range函数返回对象

range函数的返回值是一个可迭代对象,同理利用iter方法也可以得到他的迭代器 代码片段:

 
 
  1. R = range(5)

  2. I = iter(R)

  3. print(R)

  4. print(I)

运行结果:

 
 
  1. range(0, 5)

  2. <range_iterator object at 0x0000000002191A30>

2.3.4.迭代enumerate函数返回对象

enumerate方法返回的也是可迭代对象,他的迭代器就是他自身 代码片段:

 
 
  1. E = enumerate('spam')

  2. print(E)

  3. print(iter(E))

运行结果:

 
 
  1. <enumerate object at 0x00000000021F2558>

  2. <enumerate object at 0x00000000021F2558>

可迭代对象按照需求,一次只返回一个结果,而不是一口气一次性的返回一个对象列表,因此我们必须把可迭代对象包裹在一个list调用中,从而才能一次性看到它们所有的值。 代码片段:

 
 
  1. print(list(enumerate('spam')))

运行结果:

 
 
  1. [(0, 's'), (1, 'p'), (2, 'a'), (3, 'm')]


由于篇幅有限,本文为作者酱油哥(清华大学计算机硕士,泰康资管/军工央企职场经历)原创编写的Python核心基础教程》小册子中的一篇文章(节选),小册共分12小节。点击下面进入小册子,原创不易,欢迎订阅:

小册目录

第1节:深入剖析 Python 容器的使用方法

第2节:循环迭代与容器遍历用法解析

第3节:详解字符串常见用法

第4节:Python字符编码深入剖析及应用举例

第5节:Python文件操作用法探讨

第6节:Python 动态类型与对象拷贝机制分析

第7节:理顺可迭代对象、迭代器与迭代环境

第8节:生成器的使用技巧详解

第9节:函数的基本特征与变量作用域

第10节:函数参数的传递、修改、匹配与解包过程全解析

第11节:函数闭包与装饰器用法详解

第12节:异常的处理方式


640?wx_fmt=gif

Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以公安部、工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

640?wx_fmt=jpeg

▼ 点击下方阅读原文免费成为社区会员

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值