1,生成器的两种形式
- 列表推导式的最外层[]换成()就变成了生成器
In [15]: L = [ x*2 for x in range(5)] In [16]: L Out[16]: [0, 2, 4, 6, 8] In [17]: G = ( x*2 for x in range(5)) In [18]: G Out[18]: <generator object <genexpr> at 0x7f626c132db0>
- 函数的形式(函数内部只要含有yield就是一个生成器)
In [30]: def fib(n): ....: current = 0 ....: num1, num2 = 0, 1 ....: while current < n: ....: num = num1 ....: num1, num2 = num2, num1+num2 ....: current += 1 ....: yield num ....: return 'done'
- 列表推导式和生成器的区别
- 列表推导式占用空间,生成器节省空间
2, 生成器的取值方式
- 通过next()函数进行取值
- 因为生成器是特殊的迭代器,所以我们可以通过next()进行取值
- 通过生成器对象.next()取值
3, 生成器的唤醒方式
- next()函数进行唤醒
- 通过生成器对象.send()方法,该方法可以向生成器内部传值
- 接受该值的为yield左边的变量
- 首次唤醒生成器不能使用send方法,因为没有变量去接受
4, 利用生成器完成斐波那契数列
-
把函数中的return换成yield就好,当调用函数的时候,如果函数中有yield,不是调用函数,而是创建了一个生成器对象,我们可以通过for进行取值,所以生成器是一种特殊的迭代器
-
从生成器取出值的第二种方式,通过next,通过next一次只能取出一个值,函数会暂停到返回值的地方 ,当再次调用的时候,程序继续执行!!
In [30]: def fib(n): ....: current = 0 ....: num1, num2 = 0, 1 ....: while current < n: ....: num = num1 ....: num1, num2 = num2, num1+num2 ....: current += 1 ....: yield num ....: return 'done' ....: In [31]: F = fib(5) In [32]: next(F) Out[32]: 1 In [33]: next(F) Out[33]: 1 In [34]: next(F) Out[34]: 2 In [35]: next(F) Out[35]: 3 In [36]: next(F) Out[36]: 5 In [37]: next(F) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-37-8c2b02b4361a> in <module>() ----> 1 next(F) StopIteration: done
-
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
In [39]: g = fib(5) In [40]: while True: ....: try: ....: x = next(g) ....: print("value:%d"%x) ....: except StopIteration as e: ....: print("生成器返回值:%s"%e.value) ....: break ....: value:1 value:1 value:2 value:3 value:5 生成器返回值:done In [41]:
5, 生成器总结
- 生成器的两种形式(函数,列表推导式)
- 生成器的两种唤醒方式(next函数, send方法)
- 生成器两种唤醒方式的区别(send可以传值)
- 在生成器内部如何接受return的返回值(通过捕获异常的方式获取, e.value)
- send唤醒方式的注意点(首次唤醒不能使用send()方法)