什么是生成器
我们可以使用列表生成式很方便地创建一个列表,如以下代码:
In [1]: l = [ x*2 for x in range(5) ]
In [2]: l
Out[2]: [0, 2, 4, 6, 8]
如果要创建的是一个1000万个元素的列表呢?不可能使用以上的方式,即使你的电脑性能强劲,内存足够用,也不是这么用来浪费的。好比,你不可能用一个大桶来装一茶杯的水。
怎样才能满足既能实现我们的需求,又不占用大量的内存?如果储存的只是生成列表的算法,而不是具体的值,就可以实现了。
这种存储算法的数据结构就称为生成器。
创建生成器有以下几种方法
将列表生成式的[]改成()
In [3]: l = ( x*2 for x in range(5) )
In [4]: l
Out[4]: <generator object <genexpr> at 0x103ca38b8> # 生成器
打印生成器看到的只是数据类型,而不是具体的值。需要使用next()函数获得生成器的下一个返回值
In [5]: next(l)
Out[5]: 0
In [6]: next(l)
Out[6]: 2
In [7]: l.__next__()
Out[7]: 4
next() 与 生成器的 next() 方法作用相同
如果 next() 超出了生成器的数据范畴,会怎样?
In [9]: next(l)
Out[9]: 6
In [10]: next(l)
Out[10]: 8
In [11]: next(l)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-11-cdc8a39da60d> in <module>()
----> 1 next(l)
StopIteration:
当超出生成器的计算范畴,会抛出 StopIteration 异常
如果生成器的数据是用 next() 一个个调用,那会让人无比烦躁,而且还得谨防 StopIteration 异常。实际开发中,我们是用 for 去循环调用生成器
In [15]: l = ( x*2 for x in range(5) )
In [16]: for x in l:
...: print(x)
...:
0
2
4
6
8
用 yield 创建生成器
生成斐波拉契函数
In [18]: def fib(times):
...: n = 0
...: a, b = 0, 1
...: while n < times:
...: print(b)
...: a, b = b, a+b
...: n += 1
...: return 'done'
...:
...:
In [19]: fib(5)
1
1
2
3
5
Out[19]: 'done'
In [20]: def fib(times):
...: n = 0
...: a, b = 0, 1
...: while n < times:
...: yield(b) # 将print改为yield, 函数就成为了生成器
...: a, b = b, a+b
...: n += 1
...: return 'done'
...:
...:
In [21]: F = fib(5)
In [22]: next(F)
Out[22]: 1
In [23]: next(F)
Out[23]: 1
In [24]: next(F)
Out[24]: 2
In [25]: next(F)
Out[25]: 3
In [26]: next(F)
Out[26]: 5
In [27]: next(F)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-27-372178f5f53b> in <module>()
----> 1 next(F)
StopIteration: done
In [28]: F
Out[28]: <generator object fib at 0x103ca3570> # 生成器
使用for循环迭代生成器
In [7]: for x in fib(5):
...: print(x)
...:
1
1
2
3
5
for循环调用能将结果输出,可是并没有捕获到fib()的return值。返回值其实是包含在StopIteration的value中,因此必须捕获StopIteration异常
In [8]: F = fib(5)
In [9]: while True:
...: try:
...: x = next(F)
...: print(x)
...: except StopIteration as e:
...: print('返回值是:%s' %e.value)
...: break
...:
1
1
2
3
5
返回值是:done
yield的执行流程
In [20]: def fib(times):
...: n = 0
...: a, b = 0, 1
...: while n < times:
...: print('%s_before_yield' %n)
...: yield(b) # 将print改为yield, 函数就成为了生成器
...: print('%s_after_yield' %n)
...: a, b = b, a+b
...: n += 1
...: return 'done'
...:
...:
In [21]: F = fib(5)
In [22]: next(F)
0_before_yield
Out[22]: 1 # 执行到yield, 程序停住,不再往下执行,并记住当前的执行位置
In [23]: next(F)
0_after_yield
1_before_yield
Out[23]: 1 # 从上一处的停顿往下继续执行,同样到yield处继续停住
In [24]: next(F)
1_after_yield
2_before_yield
Out[24]: 2
In [25]: next(F)
2_after_yield
3_before_yield
Out[25]: 3
In [26]: next(F)
3_after_yield
4_before_yield
Out[26]: 5
In [27]: next(F) # 超出生成器的范围报错
4_after_yield
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-27-372178f5f53b> in <module>()
----> 1 next(F)
StopIteration: done