浅谈Python中的可迭代对象、迭代器、For循环工作机制、生成器


Posted in Python onMarch 11, 2019

1.iterable iterator区别

要了解两者区别,先要了解一下迭代器协议:
迭代器协议是指:对象需要提供__next__()方法,它返回迭代中的元素,在没有更多元素后,抛出StopIteration异常,终止迭代。
可迭代对象就是:实现了迭代器协议的对象。
协议是一种约定,可迭代对象实现迭代器协议,Python的内置工具(如for循环,sum,min,max函数等)通过迭代器协议访问对象,因此,for循环并不需要知道对象具体是什么,只需要知道对象能够实现迭代器协议即可。
迭代器(iterator)与可迭代对象(iterable)并不是同一个概念。

直观上:

1.可迭代对象(iterable):凡是具有__iter__的方法的类,都是可迭代的类。可迭代类创建的对象实现了__iter__方法,因此就是可迭代对象。用list、tuple等容器创建的对象,都是可迭代对象。可迭代对象通过__iter__方法返回一个迭代器,然后在内部调用__next__方法进行迭代,最后没有元素时,抛出异常(这个异常python自己会处理,不会让开发者看见)。

2.迭代器(iterator):迭代器对象必须同时实现__iter__和__next__方法才是迭代器。对于迭代器来说,__iter__ 返回的是它自身 self,__next__ 则是返回迭代器中的下一个值,最后没有元素时,抛出异常(异常可以被开发者看到)。

从上面2点可以看出:

1.迭代器一定是可迭代对象,因为它实现了__iter__()方法;

2.通过iter()方法(在类的内部就是__iter__)能够使一个可迭代对象返回一个迭代器。

3.迭代器的 __iter__ 方法返回的是自身,并不产生新的迭代器对象。而可迭代对象的 __iter__ 方法通常会返回一个新的迭代器对象。

第3点性质正是可迭代对象可以重复遍历的原因(每次返回一个独立的迭代器,就可以保证不同的迭代过程不会互相影响);而迭代器由于返回自身,因此只能遍历一次。

上面3点可以通过下面的例子看出来:

from collections import Iterable
from collections import Iterator
print isinstance(iter([1,2]),Iterator)
print isinstance(iter([1,2]),Iterable)
print isinstance([1,2],Iterator)
print isinstance([1,2],Iterable)
##result
True
True
False
True
##id可以查看一个对象在内存中的地址
test=[1,2,3]
testIter=iter(test)
print id(testIter)
print id(testIter)
print id(iter(test))
print id(iter(test))
print id(test.__iter__())
print id(test.__iter__())
##result:可迭代对象每次调用iter方法都会返回一个新的迭代器对象,而迭代器对象调用iter方法返回自身
67162576 
67162576 
67162688 
67162632 
67162856 
67163024

2.iterable的工作机制

拿一个例子看看,首先定义一个有__iter__方法,但是没有next()方法的类 (PS:在python2中是next(),python3是__next__()):

from collections import Iterable, Iterator
class Student(object):
 def __init__(self,score):
 self.score=score
 def __iter__(self):
 return iter(self.score)
 
test= Student([80,90,95])
print isinstance(test, Iterable)
print isinstance(test, Iterator)
for i in test:
 print i
##result
True
False
80
90
95
##可重复遍历
for i in test:
 print i
##result
80
90
95

上面代码的结果印证了定义中提到的:

缺少了next()方法,可迭代对象就不是迭代器。

此外,注意到:可迭代对象通过__iter__方法每次都返回了一个独立的迭代器,这样就可以保证不同的迭代过程不会互相影响。

也就是说,通过iterable可以实现重复遍历,而迭代器是无法重复遍历的!

因此,如果想要把可迭代对象转变为迭代器,可以先调用iter()方法返回一个迭代器。然后就可以用next()不断迭代了!

print isinstance(iter(test),Iterator)
testIter=iter(test)
print testIter.next()
print testIter.next()
print testIter.next()
##result
True
80
90
95
##一旦取完了可迭代对象中所有的元素,再次调用next就会发生异常
print testIter.next()
##result
StopIteration:

3.迭代器Iterator的工作机制

看下面这个例子:

class Student(object):
 def __init__(self,score):
 self.score=score
 def __iter__(self):
 return self
 
 def next(self):
 if self.score<100:
 self.score+=1
 return self.score
 else:
 raise StopIteration()
 
test= Student(90)
print isinstance(test, Iterable)
print isinstance(test, Iterator)
print test.next()
print test.next()
print test.next()
for i in test:
 print i
##result
True
True
91
92
93
94
95
96
97
98
99
100
##如果此时再对test这个迭代器调用next方法,就会抛出异常
test.next()
##result
StopIteration:

这个例子印证了定义中的:迭代器对象必须同时实现__iter__和__next__方法才是迭代器。

那么,使用迭代器好处在哪呢?

Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

一个很常见的应用就是:Python在处理列表的时候,是直接把整个列表读进内存的,当遇到大量样本时的时候会变得很慢。而迭代器的优势在于只把需要的元素读进内存,因此占用内存更少。

换句话说,迭代器是一种惰性求值模式,它是有状态的,只有在调用时才返回值,没有调用的时候就等待下一次调用。这样就节省了大量内存空间。

这个例子印证了定义中的:迭代器对象必须同时实现__iter__和__next__方法才是迭代器。

那么,使用迭代器好处在哪呢?

Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

一个很常见的应用就是:Python在处理列表的时候,是直接把整个列表读进内存的,当遇到大量样本时的时候会变得很慢。而迭代器的优势在于只把需要的元素读进内存,因此占用内存更少。

换句话说,迭代器是一种惰性求值模式,它是有状态的,只有在调用时才返回值,没有调用的时候就等待下一次调用。这样就节省了大量内存空间。

4.for循环的工作机制

有了上面2个例子,就可以总结一下在可迭代对象与迭代器中的For循环工作机制了。

当对象本身就是迭代器时,For循环工作机制:

  1. 调用 __iter__方法,返回自身self,也就是返回迭代器。
  2. 不断地调用迭代器的next()方法,每次按序返回迭代器中的一个值。
  3. 迭代到最后没有元素时,就抛出异常 StopIteration

在可迭代对象中,for循环工作机制:

  1. 先判断对象是否为可迭代对象(等价于判断有没有__iter__或__getitem__方法),没有的话直接报错,抛出TypeError异常。有的话,调用 __iter__方法,返回一个迭代器。
  2. 在python内部不断地调用迭代器的__next__方法,每次按序返回迭代器中的一个值。
  3. 迭代到最后没有元素时,就抛出异常 StopIteration,这个异常 python 自己会处理,不会暴露给开发者。

借用网络上的一张图直观理解一下:

浅谈Python中的可迭代对象、迭代器、For循环工作机制、生成器

此外,还要注意,python中的for循环其实兼容了两种机制:

  1. 如果对象有__iter__会返回一个迭代器。
  2. 如果对象没有__iter__,但是实现了__getitem__,会改用下标迭代的方式。
  3. __getitem__可以帮助一个对象进行取数和切片操作。

当for发现没有__iter__但是有__getitem__的时候,会从0开始依次读取相应的下标,直到发生IndexError为止,这是一种旧的迭代协议。iter方法也会处理这种情况,在不存在__iter__的时候,返回一个下标迭代的iterator对象来代替。一个重要的例子是str,字符串就是没有__iter__方法的,但是却依然可以迭代,原因就是其在for循环时调用了__getitem__方法。

看一个例子:

from collections import Iterable, Iterator
class Student(object):
 def __init__(self,score):
 self.score=score
 def __getitem__(self,n):
 return self.score[n]
 
test= Student([80,90,95])
print isinstance(test, Iterable)
print isinstance(test, Iterator)
print isinstance(iter(test), Iterable)
print isinstance(iter(test), Iterator)
for i in test:
 print i
##result
False
False
True
True
80
90
95
for i in range(0,3):
 print test[i]
##result
80
90
95
for i in iter(test):
 print i
##result
80
90
95

可以看到,实现了__getitem__方法的对象本身,尽管不是iterable与iterator,仍旧是可以调用for循环的。
通过iter方法,返回一个下标迭代的iterator对象。

5.generator的原理

最后说一下生成器,生成器是一种特殊的迭代器,当然也是可迭代对象。
对于生成器,Python会自动实现迭代器协议,以便应用到迭代中(如for循环,sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常。
创建生成器的方法:将return 改为yield。具体的实现网络上教程很多,不细说了。

6.总结

到一幅图片很好的描述了本文的所有内容,就拿它作为文末的总结吧!

浅谈Python中的可迭代对象、迭代器、For循环工作机制、生成器

以上所述是小编给大家介绍的Python中的可迭代对象、迭代器、For循环工作机制、生成器详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
如何处理Python3.4 使用pymssql 乱码问题
Jan 08 Python
python连接mysql实例分享
Oct 09 Python
Python和C/C++交互的几种方法总结
May 11 Python
Python实现数据库并行读取和写入实例
Jun 09 Python
Python原始字符串与Unicode字符串操作符用法实例分析
Jul 22 Python
Python中的类与类型示例详解
Jul 10 Python
python-视频分帧&amp;多帧合成视频实例
Dec 10 Python
pytorch 批次遍历数据集打印数据的例子
Dec 30 Python
Python自动创建Excel并获取内容
Sep 16 Python
Python实战之用tkinter库做一个鼠标模拟点击器
Apr 27 Python
Python+uiautomator2实现自动刷抖音视频功能
Apr 29 Python
关于python类SortedList详解
Sep 04 Python
python使用selenium实现批量文件下载
Mar 11 #Python
利用Python实现微信找房机器人实例教程
Mar 10 #Python
谈谈Python中的while循环语句
Mar 10 #Python
15行Python代码实现网易云热门歌单实例教程
Mar 10 #Python
Python如何爬取实时变化的WebSocket数据的方法
Mar 09 #Python
浅谈python的深浅拷贝以及fromkeys的用法
Mar 08 #Python
Python高级特性与几种函数的讲解
Mar 08 #Python
You might like
PHP微信开发之查询城市天气
2016/06/23 PHP
PHP一个简单的无需刷新爬虫
2019/01/05 PHP
php中的依赖注入实例详解
2019/08/14 PHP
JavaScript对象学习经验整理
2013/10/12 Javascript
js实现浮动在网页右侧的简洁QQ在线客服代码
2015/09/04 Javascript
JS+CSS实现闪烁字体效果代码
2016/04/05 Javascript
Websocket协议详解及简单实例代码
2016/12/12 Javascript
JavaScript定义函数_动力节点Java学院整理
2017/06/27 Javascript
JS基于正则表达式实现的密码强度验证功能示例
2017/09/21 Javascript
jquery ajaxfileupload异步上传插件
2017/11/21 jQuery
解决vue router组件状态刷新消失的问题
2018/08/01 Javascript
vue升级之路之vue-router的使用教程
2018/08/14 Javascript
vue使用laydate时间插件的方法
2018/11/14 Javascript
详解离线安装npm包的几种方法
2018/11/25 Javascript
vue中动态select的使用方法示例
2019/10/28 Javascript
[04:27]DOTA2官方论坛水友赛集锦
2013/09/16 DOTA
python获取当前日期和时间的方法
2015/04/30 Python
Python下的常用下载安装工具pip的安装方法
2015/11/13 Python
详解Python3操作Mongodb简明易懂教程
2017/05/25 Python
pycharm远程调试openstack的图文教程
2017/11/21 Python
pycharm 将django中多个app放到同个文件夹apps的处理方法
2018/05/30 Python
Python爬虫框架scrapy实现的文件下载功能示例
2018/08/04 Python
python2.7实现邮件发送功能
2018/12/12 Python
Python使用Turtle模块绘制国旗的方法示例
2021/02/28 Python
CSS3弹性布局内容对齐(justify-content)属性使用详解
2017/07/31 HTML / CSS
来自世界各地的优质葡萄酒:VineShop24
2018/07/09 全球购物
AJAX都有哪些有点和缺点
2012/11/03 面试题
高中自我评价范文
2014/01/27 职场文书
幼儿园户外活动总结
2014/07/04 职场文书
教师党员学习群众路线心得体会
2014/11/04 职场文书
2014年个人思想工作总结
2014/11/27 职场文书
2015年九一八事变纪念日演讲稿
2015/03/19 职场文书
大学生求职自荐信
2015/03/24 职场文书
2016年领导干部正风肃纪心得体会
2015/10/09 职场文书
Golang 编译成DLL文件的操作
2021/05/06 Golang
Python OpenCV实现图像模板匹配详解
2022/04/07 Python