可迭代的对象、迭代器和生成器
生成器
迭代
通过for
循环遍历list或tuple,这种遍历我们称为迭代(Iteration)。
enumerate( )函数:
可以把一个list变成索引-元素对,这样就可以在for
循环中同时迭代索引和元素本身
from collections import Iterable
小结
任何可迭代对象都可以作用于for
循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for
循环。
列表生成式
在一个列表/字典/集合/元组生成式中,放在for前面的if……else是表达式,而放在for后面的if是过滤条件,不能带else。
生成器
如果列表元素可以按照某种算法推算出来,那我们因此可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
generator保存的是算法,每次调用next( g ),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
创建生成器的2种方式:
1,把列表生成式中的中括号[ ] 改成小括号( )
2,yield关键字:如果推算的算法比较复杂,用类似列表生成式的for
循环无法实现的时候,还可以用函数来实现。
执行流程:
最难理解的就是generator和函数的执行流程不一样。
函数:是顺序执行,遇到return语句或者最后一行函数语句就返回。
而变成generator的函数,在每次调用next( )的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
生成器拿不到return语句的返回值
用for循环调用generator时拿不到return语句的返回值。
如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中。
迭代器
迭代器的方法有iter()和next(),本质是实现了__iter__()和__next__()方法,只要任何对象实现了这两个方法,那该对象就是一个迭代器。
把一个类作为一个迭代器使用,需要在类中实现两个方法 __iter__() 与 __next__() 。
可迭代对象Iterable:凡是可作用于for
循环的对象都是Iterable
类型,即都是可迭代对象。
迭代器Iterator:凡是可作用于next()
函数并不断返回下一个值的对象都是Iterator
类型,即都是迭代器,它们表示一个惰性计算的序列;
PS, 注意迭代器Iterator和生成器generator的区别
1,可迭代对象Iterable
可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list, tuple, dict, set, str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
2,isinstance()函数:
判断一个对象是否是Iterable
对象、Iterator
对象
#可迭代对象 from collections import Iterable #迭代器 from collections import Iterator
3,iter( )函数:
把集合数据类型list
、dict
、str
等Iterable
变成Iterator.
4,问题:为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。
可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据;
所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结
Python的for
循环本质上就是通过不断调用next()
函数实现的,而且for循环会处理stopIteration异常。
for i in [1,2,3,4,5]: pass #for循环等价于下面的函数: #首先获得Iterator对象 it=iter([1,2,3,4,5]) #循环 while True: try: #获得下一个值 print(next(it)) except StopIteration: #遇到StopIteration就退出循环 break