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 Truefor循环用来捕捉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异常,故抛出。