今天整理下 Python3 中生成器和协程的使用。
yield 生成器输出数据 python 中可以使用 yield 关键字实现生成器,简单点说就是:一个函数中,如果包含 yield 关键字,那么这个函数就是一个生成器。下面看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def test () : """生成器:yield的使用 """ flag = 0 print("=======start=======" ) for i in range(5 ): print("=====flag:" , flag) flag += 1 yield i print("========end=========" ) def main () : """调用生成器 """ t = test() print(t) print(next(t)) print(next(t)) print(next(t)) if __name__ == "__main__" : main()
上面代码的输出结果是:
1 2 3 4 5 6 7 8 9 10 <generator object test at 0x000001726581C2B0> =======start======= =====flag: 0 0 ========end========= =====flag: 1 1 ========end========= =====flag: 2 2
从上面的输出结果中可以看出,每次调用next(),都会从上一次结束的地方执行。 相当于遇到 yield 时,会跳出当前代码的执行,再次调用next()时,会从上一次跳出的地方开始执行 (即print(“========end=========”)这行代码试下一次执行的开始)。调用生成器时可以使用 next() 函数,也可以使用生成器的 __next__
() 方法来获取值。
生成器接收外部数据 在生成器中,yield 还可以接收外部传进来的值,例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 def test () : """生成器:yield接收外部传入的值 """ for i in range(2 ): x = yield i print("=====x:" , x) return "OK" def main () : t = test() try : ret = t.send(None ) print("=====ret1:" , ret) ret = t.send(9 ) print("=====ret2:" , ret) ret = t.send(8 ) print("=====ret3:" , ret) ret = t.send(7 ) print("=====ret4:" , ret) except StopIteration as exp: print("=====value:" , exp.value) if __name__ == "__main__" : main()
上面代码的输出为:
1 2 3 4 5 =====ret1: 0 =====x: 9 =====ret2: 1 =====x: 8 =====value: OK
当生成器中 yield 后没有值返回时,再调用 send() 或者 next() 会抛出 StopIteration 异常,所以我们需要捕获该异常。若生成器中有返回值,可以在捕获异常时获取它。
使用for循环遍历生成器 生成器中的值也可以通过 for 循环获取,类似于调用 next() 方法,例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def test () : """生成器 """ for i in range(5 ): yield i def main () : """使用 for 循环调用生成器 """ t = test() for i in t: print(i) if __name__ == "__main__" : main()
协程,更像是一个过程,类似线程和进程,协程是由调用方来控制程序执行和切换的过程。python 中的协程是由生成器转化而来,即通过扩展生成器的功能来实现协程。
python中,生成器和协程一样,都是使用了 yield 关键字的函数,但是通常来说,生成器仅仅是向外部输出数据,并将代码的执行交给调用方,协程不仅可以向外部输出数据,还能通过 send() 方法接收外部传进来的数据,即 yield data 和 x = yield data。yield 类似于一种控制流程的方式。
yield from 一个含有 yield from 的方法也是一个生成器,与 yield 不同的是,yield from 后面如果跟一个生成器,那么可以直接调用这个生成器,而不再需要 next() 函数。yield from 后面也可一跟一个可迭代对象,此时相比于 for 循环加 yield 输出数据要方便。例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 def test01 () : """yield from 后面可以跟一个可迭代对象 """ yield from [1 , 2 , 3 , 4 , 5 ] return "ok" def test02 () : """yield from 后面跟一个生成器 """ print("=====start=====" ) ret = yield from test01() print("=====ret:" , ret) def main () : t = test02() try : print("=====t1:" , next(t)) print("=====t2:" , next(t)) print("=====t3:" , next(t)) print("=====t4:" , next(t)) print("=====t5:" , next(t)) print("=====t6:" , next(t)) except StopIteration: pass if __name__ == "__main__" : main()
上面代码的输出结果为:
1 2 3 4 5 6 7 =====start===== =====t1: 1 =====t2: 2 =====t3: 3 =====t4: 4 =====t5: 5 =====ret: ok
可以看出,生成器 test02() 里面嵌套了一个生成器 test01()。