可以被next函数调用不断返回下一个值的对象称为迭代器(iterator),可以直接用于for循环的对象称为可迭代对象(iterable),所有的可迭代对象均可以通过iter函数转变为迭代器。事实上,for循环内部先调用iter()把iterable变成iterator然后再进行循环迭代
iterator可以用来访问集合元素,迭代器对象从集合的首元素开始访问,直到所有元素被访问完结束,只能向前访问。迭代器的一个优点是事先不需要准备好整个迭代过程中的所有元素,仅仅在迭代到某个元素时才计算该元素,在这之前或之后,元素可以不存在或被销毁。通过next()函数,可以取到每次yield,访问到最后的时候,会抛出stopiteration异常。但是,如果用户提供了default参数之后,当取出所有元素,再次调用next函数会返回default,不会抛出Stopiteration异常
>>> l=[1,2,3,4]
>>> a=iter(l)
>>> next(a)
1
>>> next(a)
2
>>> next(a)
3
>>> next(a)
4
>>> next(a)
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
next(a)
StopIteration
>>> from collections import Iterator, Iterable
>>> isinstance(a, Iterable)
True
>>> isinstance(a, Iterator)
True
>>> isinstance(l, Iterable)
True
>>> isinstance(l, Iterator)
False
对于文件对象来说,next函数也同样适用,next函数的作用就和readline类似,都是一次读取文件的一行内容。但是readline读取到末尾的时候返回的是空字符串,next函数则会引发StopIteration异常
比如:
>>> f=open(r'E:\sublimetext2\config.php')
>>> f.readline()
'<?php\n'
>>> next(f)
'\n'
使用for循环显示读取文件内容:
for line in open("config.php").readlines():
print line
for循环实现了迭代器,迭代器每次只从对象读取一条数据,不会造成过大的内存开销。但是,它实际上是把文件一次性加载到内存中再逐行打印出来。如果文件很庞大,内存的开销就大了.
更快的方法:
for line in open("config.php"):
print line
这里没有显示读取文件,而是通过迭代器读取每一行内容
或者这样写:
with open(r'E:\sublimetext2\config.php') as f:
for line in f:
print line
前面提到for循环实际上就是对可迭代对象转换为迭代器,然后通过调用next函数来实现遍历,我们可以将二者做过等价替换:
for a in [1,2,3]:
print a
<=====>
b=iter([1,2,3])
try:
while 1:
print next(b)
except StopIteration:
pass
因此我们也可以总结出:
凡是可作用于for循环的对象都是iterable类型;
凡是可作用于next()函数或者__next__()方法的都是iterator类型
自定义迭代器的大概过程是这样的:
def iter(obj):
return obj.__iter__()
#python2:
def next(obj):
return obj.next()
#python3
def next(obj):
return obj.__next__()
可迭代对象内部一定是包含了__iter__()的魔法方法,list对象内部就一定存在__iter__()方法,__iter__()就是返回iterator实例的
next()函数只是调用了对象的__next__()(或者next())方法,list对象内部一定不存在__next__()方法,迭代器一定存在__next__()
所以,当我们要将iterable转换为iterator的时候,可以使用iterator=iterable.__iter__(),比如a=l.__iter__()
当我们使用“iterable.__next__()”(比如l.__next__())的时候会报错,当使用”iterator.__next__()” (a.__next__(),python2用a.next())的时候不报错。
注意:python3中可以使用__next__()来获取下一个值,但是在python2中要用.next()
迭代器(iterator)必须至少要定义 __iter__() 和 __next__() 两个方法
class Iterator:
def __init__(self):
self.start=-1
def __next__(self):
res=self.start
self.start +=3
if res >10:
raise StopIteration
return res
def __iter__(self):
return self
>>> a=Iterator()
>>> print(list(a))
[-1, 2, 5, 8]
上面是python3版本,下面是python2版本
>>> class Iterator:
def __init__(self):
self.start=-1
def next(self):
res=self.start
self.start +=3
if res >10:
raise StopIteration
return res
def __iter__(self):
return self
>>> a=Iterator()
>>> print list(a)
[-1, 2, 5, 8]
通过上面你可以注意到__iter__的返回值都是self,事实上,当用户自定义迭代器的时候__iter__返回的就是对象自身
自定义迭代器的时候,通过在类里面定义一个__iter__和__next__方法,用第一个来返回第二个的返回对象
只要是实现了__iter__()方法的对象,就可以使用迭代器进行访问
也行会问,既然for循环实际上就是用了迭代器的原理,那么迭代器还有什么不可替代或者提供比较好的处理方案的情况呢?
1.对于那些支持随机访问的数据结构,比如list、tuple还有dict,迭代器对比for并没有优势,而且还缺失了索引值(尽管可以使用内置函数enumerate找回索引,但是也增加了繁琐度)。但是,对于无法随机访问的数据结构set(),迭代器是唯一的访问元素方式。
比如:
>>> s={'a','b','c'}
>>> a=s.__iter__()
>>> a.next()
'a'
>>> a.next()
'c'
>>> a.next()
'b'
2.节省内存空间,前面提到事先不需要准备好整个迭代过程中的所有元素,对于数据庞大的结构和文件,迭代器是一种理想的访问方式。如果我们是要对xml文件进行处理的时候,由于etree.iterparse序列化的xml tree是可迭代对象,我们可以渐进式的处理内容