
4.6 【小白也要懂】生成器
在讨论生成器之前,我们要先了解什么是迭代?迭代是访问集合元素的一种方式,是指通过重复执行的代码处理相似的数据集。它有个特点:本次迭代的处理数据要依赖上一次迭代的结果才能继续执行,上一次产生的结果为下一次产生结果的初始状态,如果中途有任何停顿,都不能算是迭代。
迭代不一定是循环,我们通过下面两个例子来解释。
例4-18 非迭代

这段程序只是连续三次输出Hello world,输出的数据不依赖上一次的数据,因此这不是迭代。
例4-19 迭代

每次输出的结果都依赖于上一次循环的结果,这就是迭代。
好,掌握了迭代的思想后,再来学习列表生成式。这又是个新事物,举个例子,现在有个需求,要创建列表[0,1,2,3,4,5,6,7,8,9],并对列表里面的每个数值都加1,我们怎么实现这个需求呢?
例4-20 列表生成式

这样就通过列表生成式实现了所有元素都加1的目标,是不是很方便?
有人会问为什么要用列表生成式?它有什么优点呢?其实直接创建一个列表,由于受到内存的限制,列表容量肯定是有限的。夸张一点说,如果我们要创建一个包含100万个数值元素的列表,常规方法无疑会占用很大的内存,而且如果仅仅只需要访问列表中的几个元素,那么绝大多数的元素占用空间都白白浪费了。如果列表元素可以按照某种算法推算出来,那就可以在循环的过程中不断推算出后续的元素。这样就不必创建完整的列表list,从而节省大量的空间。
类似这种一边循环一边计算的机制,称为生成器(generator)。注意,生成器和列表生成式运行机制相同,但是并非同一个东西。
例4-21 生成器

那么创建列表生成式和生成器的区别是什么呢?从表面看就是[]和()的区别,其实不止这些。对比例4-20和例4-21两个例子,列表生成式的返回值是列表,而生成器产生的却是生成器<generator object <genexpr> at 0x034F4C30>。
掌握了定义生成器的方法后,让我们进一步讨论生成器generator_ex遍历访问方法。很简单,想要一个个打印出来,可以通过next()函数获得generator的下一个返回值。
例4-22 打印生成器的所有元素

由此可以推测出生成器generator保存的是算法,而非数据。这句话的意思是,如果有需要生成器就计算出下一个元素的值,调用一次计算一次,直到计算出最后一个元素。没有更多的元素时,抛出StopIteration的错误,上面这样不断调用next函数是一个很不好的习惯,正确的方法是使用for循环调用。
例4-23 用for循环打印出生成器元素


用for循环遍历生成器的所有元素,除了不用重复输入next函数之外,还有一个好处就是不用担心StopIteration的错误。
此外,生成器和列表生成式也可以互相转化。
例4-24 生成器表达式

注意,虽然生成器和列表生成式可以互相转化,但是两者有着本质的区别,不可混为一谈。