生成器是一类特殊的迭代器,创建方法比自定迭代器类更加简单
使用()创建生成器
把列表生成式的 [ ] 改成 ( )
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>
对于生成器G,我们可以按照迭代器的使用方法来使用,即可以通过next()函数、for循环、list()等方法使用,当遍历完后再调用next()依然会抛出StopIteration异常
In [19]: next(G) Out[19]: 0 In [20]: next(G) Out[20]: 2 In [21]: next(G) Out[21]: 4 In [22]: next(G) Out[22]: 6 In [23]: next(G) Out[23]: 8 In [24]: next(G) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-24-380e167d6934> in <module>() ----> 1 next(G) StopIteration:
使用yield创建生成器
当算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数+yield来实现。简单来说:只要在def中有yield关键字的 就称为 生成器。此时按照调用函数的方式( 案例中为F = fib(5) )使用生成器就不再是执行函数体了,而是会返回一个生成器对象( 案例中为F ),然后就可以按照使用迭代器的方式来使用生成器了。如实现斐波那契生成器
def fibonacci(num): a, b = 0, 1 current = 0 while current < num: yield a a, b = b, a + b current += 1 return '遍历完毕.....' def main(): f1 = fibonacci(10) f2 = fibonacci(10) f3 = fibonacci(5) # for循环比遍历生成器 for i in f1: print(i) # list处理生成器 print(list(f2)) # while循环处理生成器 while True: try: print(next(f3)) except StopIteration as e: # 异常StopIteration的value属性即为生成器return的值 print(e.value) break if __name__ == '__main__': main()
运行结果为:
- 使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
- yield关键字有两点作用:
- 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
- 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
- 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
- Python3中的生成器可以使用return返回最终运行的返回值,而Python2中的生成器不允许使用return返回一个返回值(即可以使用return从生成器中退出,但return后不能有任何表达式)。
使用send唤醒
除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。如下面例子,使用send传入一个4,使数列在传入4后从4开始计算下面的值
def fibonacci(num): a, b = 0, 1 current = 0 while current < num: # ret用来接收send()传入的参数,若是next()则ret为None ret = yield a print('ret:', ret) # 当ret不为None时,则将b设置为传入的新的起始值,如4,这样下一次遍历时,a = b = 4,就会从4往下开始返回了 if ret: b = ret a, b = b, a + b current += 1 def main(): f1 = fibonacci(10) print(next(f1)) print(next(f1)) # 除了next可以获取生成器下一个值外,还可以使用send()方法,区别就是send()支持传入一个参数 # 这个传入的参数其中一个作用就是可以用来设置生成器下一次返回的起始值 # f1.send(None)等价于next(f1) print(f1.send(None)) # send(4)传入参数值4,在生成器中,使用传入参数处理想要的逻辑 print(f1.send(4)) print(f1.send(None)) print(f1.send(None)) if __name__ == '__main__': main()
运行结果为: