首先对迭代作一个简短描述:从左到右查找对象的内容可称为迭代。python中常用的迭代工具有for循环,in成员关系测试,map,sum,reduce等内置函数。
然后是介绍迭代器协议:对象要实现__next__方法,并在调用时能够返回迭代中的下一项,直至最终抛出StopIteration的异常。
再介绍可迭代对象:可迭代对象是一个庞大的概念,范围很广,主要包括三类,迭代器,序列,集合和字典。下面是可迭代对象的一个简短定义:能够实现__iter__方法的对象是可迭代对象。
自动调用__iter__方法后,返回的是一个迭代器对象。所以在使用迭代工具迭代可迭代对象时,例如for循环,都是先自动调用可迭代对象的__iter__方法,返回一个迭代器对象,接着是自动调用迭代器对象的__next__方法,返回可迭代的对象的元素。
sd = [1,3,5,6,8]
for i in sd:
print(i)
执行结果:
然后是迭代器的简短定义:符合迭代器协议的对象就为迭代器。也就是能够实现__iter__和__next__方法的对象。
迭代器与可迭代对象的区别就在于__next__方法的实现。迭代器能够通过next()方法的调用不断地返回下一个值。而列表,元组,集合等可迭代对象则没有此功能。
对列表执行next()方法发生错误。
迭代器的几种生成形式:1.对列表,集合,字符串等可迭代对象使用iter()方法,就可使之成为迭代器。
2.通过python 内置的工具包 itertools创建迭代器。其中函数包括:count, cycle, repeat…itertools中有很多用于创建迭代器的方法。有兴趣的可以自行翻阅官方文档。
3.通过在类中使用__iter__和__next__方法来构造迭代器。下面通过一个类来实现类似range()方法的功能,当然这个类(迭代器)功能很简单。
class Myrange():
def __init__(self,start_index,end_index):
self.start_index = start_index
self.end_index = end_index
# 通过实现__iter__方法来返回一个迭代器对象
def __iter__(self):
return Myiteraitor(self.start_index,self.end_index)
class Myiteraitor():
def __init__(self,start,end):
self.start = start
self.end = end
# 通过实现__next__方法返回迭代器中的元素
def __next__(self):
if self.start < self.end:
result = self.start
self.start += 1
return result
else:
raise StopIteration("迭代完成")
if '__name__ == __main__':
for i in Myrange(2,5):
print(i)
执行结果如下:
其实python 的许多内置类型也是迭代器。例如python的enumerate和reversed对象是迭代器;zip,filter 和map也是迭代器;文件对象也是迭代器。
由于生成器也是迭代器的一种,所以通过生成器表达式和生成器函数也可生成迭代器。这个下面说。
使用迭代器要注意它的重要特性:惰性。只能使用一次,只能循环遍历一次。第二次便只会得到空值,这个在生成器下面再细说。由于再我们调用next()方法之前,迭代器不会做任何事情,因此可以创建无限长的迭代器,但不能创建无限长的列表和集合等,那样会占满你的内存。
上面的next()方法本质也是在调用迭代器对象内部的__next__方法。
下面是关于生成器的详细描述:
前面说了,生成器也是迭代器的一种,可以由生成器表达式和生成器函数生成。
下图是生成器表达式的形式:
s就是一个生成器,表达式和列表推导式很像,只不过是把方括号换成圆括号。
下图是一个生成器函数的形式,功能很简单:
在函数中用yield 关键字替换return,该函数就成为一个生成器。因此就可以把yield 看成return,只是普通函数在调用后会立刻执行,而生成器在调用时不会立刻执行,只有执行next()方法或遍历它时才会真正执行。
return和yield还有一个区别是,普通函数在遇到return后会立刻结束,而生成器在执行后遇到yield会停下来(在yield把它后面的值返回后才会停下来,yield有return的功能),直到下一次再次使用next()方法,才会从上次停止的地方继续执行。
注意,for循环的本质也是不断调用next()方法,从可迭代对象中获得元素。
前面说了迭代器的重要特性:惰性,迭代器里的元素只能被使用一次,再次使用就找不到了。
下面以生成器为例:
如果说4在nums里,你肯定会说一定。
那我再问一遍呢?
答案是否定的,因为前面说过,in成员测试也是迭代工具,由于迭代器里的数据只能被使用一次,在使用in迭代后,里面的数据自然也就消失了。
完。