Python协程一点理解
- 2020 年 1 月 16 日
- 笔记
yield
def test(): for i in 'abc': yield i for i in [1, 2, 3]: yield i if __name__ == '__main__': gen = test() print(gen.send(None)) print(gen.send(None)) print(gen.send(None)) print(gen.send(None)) print(gen.send(None)) print(gen.send(None)) print(gen.send(None))
def test(): for i in 'abc': yield i for i in [1, 2, 3]: yield i if __name__ == '__main__': gen = test() for i in gen: print(i)
使用for循环生成器,因为for循环可以捕捉StopIteration异常。
yield from
上面的代码也可以用yield from实现
def test(): yield from 'abc' yield from [1, 2, 3] if __name__ == '__main__': gen = test() for i in test(): print(i)
yield from
后面需要加的是可迭代对象,它可以是普通的可迭代对象,也可以是迭代器,甚至是生成器。
使用yield from实现实时计算器
def average_gen(): """ 子生成器 """ average = 0 total = 0 count = 0 while True: num = yield average if num is None: break count += 1 total += num average = total / count return average, count, total def proxy_gen(): """ 委派生成器 """ while True: average, count, total = yield from average_gen() print(f'平均值{average}, 计算{count}次, 总和{total}') def client(): """ 调用端 """ calc_average = proxy_gen() calc_average.send(None) # next(calc_average) # 预激活协程 print(calc_average.send(10)) print(calc_average.send(20)) print(calc_average.send(30)) calc_average.send(None) # 关闭协程 if __name__ == '__main__': client()
在委派生成器中的while True可以替换成for循环,循环的大小决定调用端可以calc_average.send(None)的次数(第一次预激活也计入)。
while True和for循环用来捕捉StopIteration异常,可以查看第一段代码。
示例
def average_gen(): """ 子生成器 """ average = 0 total = 0 count = 0 while True: num = yield average if num is None: break count += 1 total += num average = total / count return average, count, total def proxy_gen(): """ 委派生成器 """ for i in range(2): average, count, total = yield from average_gen() print(f'平均值{average}, 计算{count}次, 总和{total}') def client(): """ 调用端 """ calc_average = proxy_gen() calc_average.send(None) # next(calc_average) # 预激活协程 print(calc_average.send(10)) print(calc_average.send(20)) print(calc_average.send(30)) calc_average.send(None) # 关闭协程 print(calc_average.send(10)) print(calc_average.send(20)) print(calc_average.send(30)) calc_average.send(None) if __name__ == '__main__': client()
示例最后会抛出StopIteration异常。
因为在第二次calc_average.send(None)时,for循环捕捉了一次异常,进入了最后一次循环,在calc_average.send(None)时,for不能再次循环,不能处理StopIteration异常,故抛出。