本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!
作者| 慕课网精英讲师 朱广蔚
要求生成一个包含很多元素的序列,假设:
Python 提供了列表推导用于生成列表,下面使用列表推导生成一个包含 0 到 4 之间所有整数的列表,代码如下:
>>> list = [i for i in range(4)]
>>> list
[0, 1, 2, 3]
代码块123
如果生成一个从 0 到 1G 的列表,代码如下:
>>> N = 1024 * 1024 * 1024
>>> list = [i for i in range(N)]
Traceback (most recent call last):File "", line 1, in File "", line 1, in
MemoryError
代码块123456
使用列表推导创建包含 1G 个整数的列表时,需要为这 1G 个整数分配至少 4G 的内存,需要消耗大量的内存,超出了 Python 的限制,因此出现了 MemoryError 的错误。
另外,创建这个巨大的列表需要消耗大量的时间,因此执行第 2 行的语句后,系统失去响应,大约 10 多秒后才出现错误信息。
列表推导需要一次性的为 1G 个整数分配内存空间,带来了两个问题:
Python 提供了一种动态计算的思路解决以上问题,它的思想如下:
创建一个输出从 0 到 1G 的生成器,代码如下:
>>> N = 1024 * 1024 * 1024
>>> generator = (i for i in range(N))
>>> next(generator)
0
>>> next(generator)
1
>>> next(generator)
2
代码块12345678
注意:在第 2 行,创建一个输出从 0 到 1G 的序列的生成器,因为不需要分配内存,创建生成器的速度非常快,几乎是瞬间完成的。与之相比,在上一节中创建一个输出从 0 到 1G 的序列的列表,因为需要分配内存,创建列表的速度非常慢,并且导致了 MemoryError。
在 Python 中,生成器是一个特殊的对象,它按照一定的规则依次输出数据。Python 的内置函数 next(generator) 通知生成器输出一个新的数据,当生成器输出全部数据后,产生一个特殊的异常 StopIteration,用于标记生成器输出结束。
下面的代码创建一个产生 0 到 3 之间所有整数的生成器:
>>> generator = (i for i in range(3))
>>> next(generator)
0
>>> next(generator)
1
>>> next(generator)
2
>>> next(generator)
Traceback (most recent call last):File "", line 1, in
StopIteration
代码块1234567891011
根据生成器的原理,可以循环的调用 next(generator) 输出全部的序列,示例如下:
generator = (i for i in range(3))while True:try:item = next(generator)print(item)except StopIteration:break
代码块12345678
运行程序,输出结果如下:
0
1
2
代码块123
通常使用 for 循环访问生成器,示例如下:
generator = (i for i in range(3))for item in generator:print(item)
代码块1234
运行程序,输出结果如下:
0
1
2
代码块123
可以使用类似于列表推导的语法创建一个生成器,语法如下:
(expression for i in iterable)
代码块1
该生成器遍历对象 iterable,依次产生数据 expression,它的工作流程如下:
for i in iterable:generate expression
代码块12
注意:创建生成器的语法与列表推导的语法相似,不同之处在于,创建生成器的语法采用小括号 (),创建列表的语法采用方括号 []。
通过推导创建生成器的示例如下:
generator = (i*2 for i in range(5))
for i in generator:print(i)
代码块123
运行程序,输出结果如下:
0
2
4
6
8
代码块12345
可以使用类似于列表推导的语法创建一个生成器,语法如下:
(expression for i in iterable if condition)
代码块1
该生成器遍历对象 iterable,如果条件 condition 为真,则产生数据 expression,它的工作流程如下:
for i in iterable:if condition:generate expression
代码块123
通过复杂推导创建生成器的示例如下:
generator = (i for i in range(10) if i % 2 == 0)
for i in generator:print(i)
代码块123
运行程序,输出结果如下:
0
2
4
6
8
代码块12345
在生成器的生命周期中,生成器根据一定的规则产生一系列的数据,生成器可以使用 yield 关键字产生一个数据。例如,一个生成特定范围内的奇数序列的函数:
def generateOddNumbers(n):for i in range(n):if i % 2 == 1:yield igenerator = generateOddNumbers(10)
for i in generator:print(i)
代码块12345678
运行该程序,输出如下:
1
3
5
7
9
代码块12345
注意:包含 yield 关键字的函数被称为生成器函数,调用生成器函数会返回一个生成器。在上面的例子中,函数 generateOddNumbers(n) 包含 yield 关键字,是一个生成器函数,它返回一个生成器,该生成器产生从 0 到 n 范围内的奇数。
通过单链表实现堆栈,图示如下:

在上图中,每个节点有两个字段: item 和 next,item 用于存储数据,next 指向下一个节点,head 指针指向堆栈的顶部。描述堆栈的 Python 代码如下:
class Node:def __init__(self, item):self.item = itemself.next = Noneclass Stack:def __init__(self):self.head = Nonedef push(self, item):node = Node(item)node.next = self.headself.head = nodestack = Stack()
stack.push('a')
stack.push('b')
stack.push('c')
代码块123456789101112131415161718
def stackGenerate(stack):cursor = stack.headwhile cursor != None:yield cursor.itemcursor = cursor.next
代码块12345
使用 while 循环显式的使用 next、StopIteration 完成对 stack 的遍历,代码如下:
generator = stackGenerate(stack)
while True:try:item = next(generator)print(item)except StopIteration:break
代码块1234567
程序依次压入 ‘a’、‘b’、‘c’,遍历时以压入相反的顺序输出,结果如下:
c
b
a
代码块123
通过 for … in 循环对生成器进行遍历,代码如下:
generator = stackGenerate(stack)
for item in generator:print(item)
代码块123
与上一节的代码相比,代码要简洁很多,程序输出相同的结果如下:
c
b
a
代码块123
欢迎关注「慕课网」,发现更多IT圈优质内容,分享干货知识,帮助你成为更好的程序员!