“迭代”是程序设计中的一个非常重要概念,它可以简单地理解为“重复地做一些事情”。Python提供了迭代器和生成器的功能,但这些概念不易掌握。这篇文章主要是总结下我个人的一些理解。不对之处,恳请各位批评指正。下面所有代码的运行环境为Python 3.6.8。

一. 迭代的必要性

Python程序设计中,我们为什么要学习迭代?首先,采用迭代可以方便地遍历集合类对象(list、tuple、range、set、dict等)。其次,迭代采用延迟计算,可以降低存储空间的要求。

Python的循环只有while 和 for...in...两种结构。通过for......in结构可以方便地遍历集合类对象中的所有元素。如下所示

python for 循环的迭代次数 python迭代器和for循环哪个快_迭代器

Python中的for...in...结构中必须包含可迭代对象,其运行机制稍后会讲到。

下面再看一个比较极端一点的例子,假如说,我程序中需要用到0~100000000的平方。直观的方案是分别计算这100000000个数的平方并按序存储在一个列表中。毫无疑问,这会造成巨大的内存开销。另一种方案是采用生成器,延迟计算。也就是说,我们并不提前计算和保存每个数的平方,而只是保存生成下一个数平方的算法。只有真正用到时,我们才调用算法进行计算。这种方案显著降低了内存开销。如下图所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_python for 循环的迭代次数_02

 

二. 可迭代对象

1. 可迭代对象的概念

Iterable类型的对象称为可迭代对象。Python通过isinstance(p, Iterable),来判断一个对象p是否为可迭代对象。那么什么样的对象才是可迭代对象,一句话概括就是“凡是实现了__iter__方法的对象就是可迭代对象”。

Python中的list、tuple、set、dict、range等集合类的对象都是可迭代对象。如下例所示,我们首先定义了一个列表对象l。通过dir可查看l所有的成员变量和成员方法,可以发现l实现了__iter__方法。isinstance(l,Iterable)结果为True,即l是一个可迭代对象。

python for 循环的迭代次数 python迭代器和for循环哪个快_Python_03

再举一个例子。我们首先定义一个Student类,其中实现了__iter__方法(空函数)。然后,生成Student类的一个对象Jack。Jack测试为可迭代对象。

python for 循环的迭代次数 python迭代器和for循环哪个快_python for 循环的迭代次数_04

2. 可迭代对象的使用

可迭代对象,常用于for...in...结构,用于遍历所有数据,也用于高阶函数map/reduce/filter等高阶函数中。

三. 迭代器对象

1. 迭代器对象的概念

Iterator类型的对象称为迭代器对象。Python中通过isinstance(p, Iterator),来判断一个对象p是否为迭代器对象。那么什么样的对象才是可迭代对象呢?一句话概括就是“凡是实现了__iter__方法和__next__方法的对象就是迭代器对象”。通过比较第二部可迭代对象的概念,可以发现:迭代器对象一定是可迭代对象;反之,则未必。

Python中的list、tuple、set、dict、range等集合类对象未实现__next__方法。因此,它们都不是迭代器对象。如下所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_生成器_05

但是,调用对象l和d的__iter__函数,则会返回一个迭代器对象。至于__iter__函数具体是如何实现的,我们这里并不关心。

python for 循环的迭代次数 python迭代器和for循环哪个快_生成器_06

为了进一步说明这个问题,我们自定义A,B,C三个类。其中,A类只实现了__iter__方法,B类只实现了__next__方法,C类同时实现了__iter__方法和__next__方法。然后,我们分别实例化这三个类的对象a,b,c。经测试发现,a为可迭代对象,但不是迭代器对象;b既不是可迭代对象,也不是迭代器对象;c是可迭代对象,也是迭代器对象。如下所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_迭代器_07

2. 迭代器对象的使用

迭代器对象,可以调用__next__函数,不断返回下一个数据,直到没有数据时抛出StopIteration错误。

python for 循环的迭代次数 python迭代器和for循环哪个快_生成器_08

python for 循环的迭代次数 python迭代器和for循环哪个快_可迭代对象_09

四. 生成器对象

1. 生成器对象的概念

生成器对象,顾名思义就是生成器(generator)类型的对象。可以通过type(p)查看对象p是否为生成器对象。Python中提供了两种方法来构造生成器对象。

一种方法是采用生成器表达式。这只需要讲列表生成式中的"[ ]"替换为“()”,如下所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_迭代器_10

另外一种方法是采用带yield关键字的生成器函数。如下所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_python for 循环的迭代次数_11

2. 生成器对象的使用

生成器对象中(隐含)实现了__iter__和__next__方法,因而也是迭代器对象。可以通过调用__next__函数返回下一个数据。采用带yield的函数创建的生成器对象,在每次调用__next__函数时开始执行,遇到yield语句就返回。再次调用时,从上次返回的yield语句处继续执行。如下所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_迭代器_12

五. 可迭代对象、迭代器对象和生成器对象的关系

生成器对象必定是迭代器对象,迭代器对象又必定是可迭代对象。三者之间存在一种包含关系,如下图所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_生成器_13

六. for ... in ... 结构的工作过程

可迭代对象常用在for...in...结构中,其工作过程是:首先会调用可迭代对象的__iter__方法,应返回一个迭代器对象。然后,for循环会不断调用所返回迭代器对象的__next__方法。直到遇到StopIteration错误时退出循环。如下所示:

python for 循环的迭代次数 python迭代器和for循环哪个快_生成器_14