9.7.1 创建生成器
生成器创建起来与函数一样简单。你现在肯定厌烦了老套的斐波那契数列,所以下面换换口味,创建一个将嵌套列表展开的函数。这个函数将一个类似于下面的列表作为参数:
nested = [[1, 2], [3, 4], [5]]
换而言之,这是一个列表的列表。函数应按顺序提供这些数字,下面是一种解决方案:
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element
这个函数的大部分代码都很简单。它首先迭代所提供嵌套列表中的所有子列表,然后按顺序迭代每个子列表的元素。倘若最后一行为print(element),这个函数将容易理解得多,不是吗?
在这里,你没有见过的是yield语句。包含yield语句的函数都被称为生成器。这可不仅仅是名称上的差别,生成器的行为与普通函数截然不同。差别在于,生成器不是使用return返回一个值,而是可以生成多个值,每次一个。每次使用yield生成一个值后,函数都将冻结,即在此停止执行,等待被重新唤醒。被重新唤醒后,函数将从停止的地方开始继续执行。
为使用所有的值,可对生成器进行迭代。
>>> nested = [[1, 2], [3, 4], [5]]
>>> for num in flatten(nested):
... print(num)
...
1
2
3
4
5
或
>>> list(flatten(nested))
[1, 2, 3, 4, 5]
简单生成器
在Python 2.4中,引入了一个类似于列表推导(参见第5章)的概念: 生成器推导(也叫生成器表达式)。其工作原理与列表推导相同,但不是创建一个列表(即不立即执行循环),而是返回一个生成器,让你能够逐步执行计算。
>>> g = ((i + 2) ** 2 for i in range(2, 27))
>>> next(g)
16
如你所见,不同于列表推导,这里使用的是圆括号。在像这样的简单情形下,还不如使
用列表推导;但如果要包装可迭代对象(可能生成大量的值),使用列表推导将立即实例化一
个列表,从而丧失迭代的优势。
另一个好处是,直接在一对既有的圆括号内(如在函数调用中)使用生成器推导时,无需
再添加一对圆括号。换而言之,可编写下面这样非常漂亮的代码:
sum(i ** 2 for i in range(10))
领取专属 10元无门槛券
私享最新 技术干货