很多小伙伴对Python的迭代器、可迭代对象和生成器的概念有点困惑。朋友帮忙。1迭代器协议迭代器协议是核心。明白了这一点,上面的概念就很容易理解了。所谓迭代器协议要求迭代器实现以下两个方法iterator.__iter__()返回迭代器对象本身。iterator.__next__()从容器中返回下一个项目。也就是说,一个对象只需要支持上面的两个方法就是迭代器。__iter__()需要返回迭代器本身,而__next__()需要返回下一个元素。2可迭代对象知道了迭代器的概念,那么什么是可迭代对象呢?这个比较简单,只要对象实现了__iter__()方法并返回一个迭代器,那么这个对象就是一个可迭代对象。比如我们常见的list是一个可迭代对象>>>l=[1,3,5]>>>iter(l)使用iter()会调用对应的__iter__()方法,返回是一个列表迭代器,所以列表是一个可迭代对象。3手写一个迭代器实现一个迭代器有不同的方法。相信大家第一个想到的就是自定义类。让我们从这个开始。为了说明,我们手动编写一个迭代器来生成奇数序列。根据迭代器协议,我们实现了以上两个方法。classOdd:def__init__(self,start=1):self.cur=startdef__iter__(self):returnselfdef__next__(self):ret_val=self.curself.cur+=2returnret_val在终端中,我们instance转换一个Odd类得到一个对象odd>>>odd=Odd()>>>odd<__main__.Oddobjectat0x101a1d9b0>使用iter()方法会调用类中的__iter__方法得到自己>>>iter(odd)<__main__.Oddobjectat0x101a1d9b0>使用next()方法会调用对应的__next__()方法获取下一个元素>>>next(odd)1>>>next(odd)3>>>next(odd)5其实odd对象就是一个迭代器。我们可以用for来遍历它odd=Odd()forvinodd:print(v)细心的小伙伴可能会发现这样其实会无限打印,那么怎么解决呢?我们拿一个列表做实验,先获取它的迭代器对象>>>l=[1,3,5]>>>li=iter(l)>>>li然后手动获取一个元素,直到没有下一个元素,看看会发生什么>>>next(li)1>>>next(li)3>>>next(li)5>>>next(li)Traceback(最近一次调用最后一次):File"",line1,inStopIteration当没有下一个元素时,原始列表迭代器将抛出StopIteration异常。估计for语句就是根据这个异常来判断是否结束的。我们修改一下原代码,生成指定范围内的奇类Odd:def__init__(self,start=1,end=10):self.cur=startself.end=enddef__iter__(self):returnselfdef__next__(self):ifself.cur>self.end:raiseStopIterationret_val=self.curself.cur+=2returnret_val让我们试试for>>>odd=Odd(1,10)>>>forvinodd:...print(v)...13579果然不出所料。我们用一个while循环来模拟for的执行过程Targetcodeforviniterable:print(v)翻译代码iterator=iter(iterable)whileTrue:try:v=next(iterator)print(v)exceptStopIteration:break其实Python中for语句的原理是一样的。for可以理解为语法糖。4其他创建迭代器的方式生成器其实就是迭代器,所以可以使用生成器的创建方式来创建迭代器。4.1生成器函数的返回与普通函数的返回不同。生成器函数使用yield。>>>defodd_func(start=1,end=10):...forvalinrange(start,end+1):...ifval%2==1:...yieldval...>>>of=odd_func(1,5)>>>of>>>iter(of)>>>next(of)1>>>next(of)3>>>next(of)5>>>next(of)Traceback(最近调用last):文件“”,第1行,在StopIteration4.2生成器表达式>>>g=(vforvinrange(1,5+1)ifv%2==1)>>>gat0x101a142b0>>>iter(g)at0x101a142b0>>>>next(g)1>>>next(g)3>>>next(g)5>>>next(g)Traceback(最近一次调用last):文件“”,第1行,在StopIteration4.3如何选择到目前为止,我们知道了3种创建迭代器的方式,那么如何选择呢?不用说,最简单的就是生成器表达式,如果表达式能满足需求,那就是;要添加更复杂的逻辑,请选择生成器功能;如果前两个不能满足要求,则用自定义类实现。总之,选择最简单的方式。5迭代器的特点5.1惰性迭代器不预先计算所有元素,而是在需要时计算并返回。5.2支持无限元素比如我们上面创建的第一个Odd类,它的实例odd表示所有大于start的奇数,列表等容器不能容纳无限元素。5.3节省空间比如保存10000个元素>>>fromsysimportgetsizeof>>>a=[1]*10000>>>getsizeof(a)80064list占用80K左右。那么迭代器呢?>>>fromitertoolsimportrepeat>>>b=repeat(1,times=10000)>>>getsizeof(b)56只需要56个字节。正是因为迭代器的惰性,才有了这个优势。6一些需要注意的细节6.1迭代器也是一个可迭代对象,因为迭代器的__iter__()方法返回的是自己,而它本身恰好是一个迭代器,所以迭代器也是一个可迭代对象。6.2迭代器遍历一次后,无法从头开始。看一个奇怪的例子>>>l=[1,3,5]>>>li=iter(l)>>>li>>>3inliTrue>>>3inliFalse因为li是一个列表迭代器,第一次搜索3的时候,找到了,所以返回True,但是因为第一次迭代,已经跳过了元素3,第二次就找不到了,所以会返回False出现。因此,请记住迭代器是“一次性”的。当然,list是一个可迭代对象,无论查找多少次都是正常的。(如果难以理解,想想上面for语句的执行原理,每次都会通过iter()方法从可迭代对象中获取一个新的迭代器)>>>3inlTrue>>>3inlTrue7section是实现迭代器协议的对象,都是实现了__iter__()方法并返回迭代器的迭代器。返回迭代器的对象是一个可迭代对象。生成器也是一种迭代器。创建迭代器的方式有三种,生成器表达式,生成器函数,自己定义类,看情况选择最简单的。迭代器也是一个可迭代对象。迭代器是“一次性”的。前三个小项是重点。明白了这三点,其他的也就明白了。弄清楚题目中名词的概念是没有问题的。8参考https://docs.python.org/3/lib...https://opensource.com/articl...http://treyhunner.com/2018/06...