什么是生成器
我们可以使用列表生成式很方便地创建一个列表, 如以下代码:
- 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
来源: http://www.jianshu.com/p/2554f43b3e4f