购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.5 迭代器和生成器

本节将介绍Python的一个重要特性: 生成器 (generator)。生成器能使代码变得简洁,在一些场合能大大地减少对内存的要求。

下面这个例子是生成前一百个 斐波那契数列 (Fibonacci sequence),并且在有需要时将数列的元素逐个显示到屏幕上。首先尝试用普通的函数方法来实现这一功能,代码如下所示。

然而采用函数去实现有两个问题:第一个是需要用一个列表去存储这个斐波那契数列,第二个是输出这个列表的元素时,用户并不能随意地中断斐波那契数列输出的过程。而Python中的生成器可以很方便地解决这两个问题。

在介绍生成器之前,先介绍关于 迭代器 (iterator)和 可迭代对象 (iterable)的相关知识。生成器和迭代器以及可迭代对象密切相关。本书第1章介绍的一些数据序列,包括列表、元组、range()对象和字符串都是可迭代对象。可迭代对象经常用在for循环中,用于遍历数据序列中的所有元素。以下代码回顾了使用列表、元组、range()对象和字符串实现在for循环中的迭代。

上述的可迭代对象,可使用iter()函数转化为迭代器。迭代器可以记住当前遍历所在位置。迭代器中的对象元素可以通过next()函数一个一个地按顺序访问。如图2-12所示为使用迭代器和next()函数进行迭代的过程。下面代码则是一个迭代器使用的例子。

图2-12 最基本的迭代器

上述提到的列表、元组、range()对象和字符串均是可迭代对象,这应该是比较容易理解的,因为在第1章中已经讲过这些数据序列都是有序的序列,其内部的元素是有序号的。在Python 3.6版本后,集合也可以通过iter()转换为迭代器,其内部的次序按照集合定义时的次序给定。字典则不能通过iter()转化为迭代器,读者可尝试运行以下代码。

生成器实质是迭代器的一种。生成器有两种形式,一种是 生成器表达式 (generator expression),另一种是 生成器函数 (generator function)。生成器表达式和列表推导式很像,因此生成器表达式可以使用类似列表推导式的生成方法,但是列表推导式使用中括号,而生成器表达式使用圆括号。请读者运行以下代码。

在生成生成器表达式后,可以通过函数next()访问生成器表达式中的值。值得注意的是,当生成器表达式中的所有值都被遍历完后,会给出“StopIteration”的消息。

生成器表达式使用圆括号是有其特别意义的。许多函数的后面都是加圆括号,而部分函数是支持迭代器协议的,如sum()、max()和min()等。因此在使用这些支持迭代器协议的函数时不需要先使用列表推导式,再使用这些函数。如以下的例子所示。

>> sum(x ** 2 for x in range(4))#use a generator
>> sum([x ** 2 for x in range(4)])#use a list

生成器表达式的另外一种表达形式是生成器函数。以下代码通过生成器函数来生成前一百个斐波那契数列。生成器函数以def开头,使用yield来返回待返回的值。

在上例中,Fib是一个生成器对象,而不是一个列表,这个生成器对象存储的是生成斐波那契数列的算法,这样极大地降低了对内存存储的要求。在上例中,还使用了try…except…语句来捕获StopIteration。这是因为使用next()函数来遍历生成器中的元素时,遍历完所有元素后,将会返回“StopIteration”提示。当遍历所有元素并捕捉到“StopIteration”后使用break跳出循环。

尽管生成器表达式和生成器函数很实用,但只能使用一次,这是因为生成器中的元素只能遍历一次。生成器使用完一次以后,若再次遍历生成器,则不会有任何输出。具体请运行以下代码查看。 +rkSm81CKcSxqNY9Z49hU6/Ad7mfiphJIQW/uBYXfreohw7A8/9B5XBgE+sadN6m

点击中间区域
呼出菜单
上一章
目录
下一章
×