我爱编程

迭代器(Iterator)

2018-05-26  本文已影响0人  江山画_孤影

迭代是Python最强大的功能之一。先给大家看张图(图来自网络),看不懂没关系,等学完这一章和下一章的内容,最后再回过头看这张图就很清晰了。

可迭代对象,迭代器,生成器关系概览

容器(container)

常见的容器对象有:
str
list, deque, ….
tuple, namedtuple, …
dict, defaultdict, OrderedDict, Counter, ….
set, frozensets, ….

可迭代对象(iterable)

#可迭代对象定义【重点】
如果对象能实现返回迭代器的__iter__的方法,或者能实现__getitem__方法,
而且参数是从零开始的索引,这种对象就是可迭代对象。

任何Python序列都可迭代的原因是:它们都实现了 __getitem__ 方法。
标准的序列不仅实现了 __getitem__ 方法同时还实现了 __iter__ 方法。

#判断对象是否是可迭代对象
#方法一 isinstance()
>>>from collections import Iterable  
>>>mydict = {'a':97,'b':98,'c':99} 
>>>isinstance(mydict,Iterable)
True  

>>>nums = map(lambda x:x*2,[1,2,3,4])
>>>isinstance(nums,Iterable)
True  

>>>mylist = ['a','b','c','d'] 
>>>isinstance(enumerate(mylist),Iterable)
True  
以及等等.......

#方法二 iter()
>>>mylist = [1,2,3,4]
>>>iter(mylist)
<list_iterator object at 0x01DC94F0>

>>>iter(mydict)
<dict_keyiterator object at 0x005C4420>

>>>iter(nums)
<map object at 0x00359FB0>

>>>iter(enumerate(mylist))
<enumerate object at 0x0213FCD8>

>>>x = 123
>>>iter(x)
TypeError: 'int' object is not iterable
#不可迭代的对象,会报错.

此外还有更多的对象同样也是可迭代对象,比如打开的状态的files,sockets等等
简单来说:绝大多数的容器都是可迭代对象,但也有例外,比如Bloom filter
虽然Bloom filter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,
因为Bloom filter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。

迭代器( iterator)

#迭代器协议
所谓的迭代器协议是指:对象需要提供__next__方法,
它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

#迭代器定义
实现了迭代器协议的对象就是迭代器

在python语言内部,迭代器用于支持:
1.for 语句
2.构建和扩展集合类型
3.逐行遍历文本文件
4.列表推导,字典推导,集合推导
5.元组拆包
6.调用函数时,使用*拆包实参

#for语句原理【重点】
在python中,for语句用于迭代,而while语句才是用于真正的循环

下面这个过程背后其实是有迭代器的,只不过我们看不到:
>>>s = 'abc'
>>>for i in mystring:
>>>    i

真实过程用while语句改写是这样:
>>> s = 'abc'
>>> it = iter(s) #通过iter方法从可迭代对象s中获取迭代器对象it
>>>while True:
>>>    try:
>>>        next(it) #在迭代器上调用next(),获取下一个值
>>>    except StopIteration: #如果没有值了,抛出StopIteration异常
>>>        del it #释放对it的引用,即废弃迭代器对象
>>>    break #退出循环

小结【重点】
在for语句内部,实际是通过调用iter()方法将可迭代对象转换成迭代器对象,
然后再重复调用next()方法实现的.for语句会自动捕获 StopIteration 异常,
并在捕获异常后终止迭代.所以,对容器对象调用 iter() 方法再使用 for 语句是多余的,比如:
>>>for i in iter(list) #多此一举

可迭代对象与迭代器的关系【重点】

1.从可迭代的对象中获取迭代器对象

>>>mylist = [1,2,3,4]  #可迭代对象
>>>iter(mylist)  #通过iter()方法从可迭代对象mylist中获取迭代器对象
<list_iterator object at 0x01D49FB0>

2.可迭代对象不是迭代器,但是所有的迭代器都是可迭代对象

#可迭代的对象一定不能是自身的迭代器.也就是说,可迭代的对象
#必须实现 __iter__ 方法,但不能实现 __next__ 方法

>>>mylist = [1,2,3,4]
>>>next(mylist)
TypeError: 'list' object is not an iterator

>>>from collections import Iterable
>>>mylist = [1,2,3,4]
>>>isinstance(iter(mylist),Iterable)
True 

#因为迭代器实现了__iter__方法,根据可迭代对象的定义,迭代器自然是可迭代对象
#而且特殊的一点是迭代器的__iter__ 方法必须返回自身
>>>iter([1,2,3,4]) #迭代器对象
<list_iterator object at 0x00619450>
>>>iter(iter([1,2,3,4])) #对迭代器对象使用iter(),返回自身
<list_iterator object at 0x00619450>


3.迭代器只能使用一次,如果想再次迭代,要重新构建迭代器。

>>>mylist = [1,2]
>>>it = iter(mylist)
>>>next(it)  #1
>>>next(it) #2
>>>next(it) #抛出异常,废弃迭代器对象,也就是it不能再用了



>>>it1 = iter(mylist)
>>>id(it1)   #6264048
>>>next(it1)
>>>next(it1)
>>>it2 = iter(mylist) #再次创建迭代器对象
>>>id(it2)   #6266800
>>>next(it2)
>>>next(it2)
it1和it2都是迭代器对象,但这两个对象显然不是同一个对象
>>>it1 is it2
False

>>>it2 = iter(it1) #传入迭代器本身没用,必须传入之前构建迭代器的可迭代对象即mylist
>>>it2
<list_iterator object at 0x004E9FB0>

#小结
因为迭代器只需 __next__ 和 __iter__ 两个方法,所以除了调用
next() 方法,以及捕获 StopIteration 异常之外,没有办法检查是否
还有遗留的元素。此外,也没有办法“还原”迭代器。如果想再次迭代,
那就要调用 iter(...),传入之前构建迭代器的可迭代对象。传入迭代
器本身没用,因为前面说过 Iterator.__iter__ 方法的实现方式是返
回实例本身,所以传入迭代器无法还原已经耗尽的迭代器。
上一篇下一篇

猜你喜欢

热点阅读