Python-协程

协程

迭代器

  • 可迭代(Iterable):直接作用于for循环的变量
  • 迭代器(Iterator):不但可以作用于for循环,还可以被next调用
    • 注意:可迭代对象不一定是迭代器。list是典型的可迭代对象,但不是迭代器
      #可迭代
      l = [i for i in range(10)]
      #l是可迭代的,但不是迭代器
      for idx in l:
      	print(idx)
      
      # range是一个迭代器
      for i in range(10):
      	print(i)
      
    • 可以通过isinstance来判断
      # isinstance案例
      # 判断是否可以迭代
      for collections import Iterable
      ll = [1,2,3,4,5]
      
      print(isinstance(ll,Iterable))
      
      # 判断是否是迭代器
      from collections import Iterator
      print(isinstance(ll,Iterator))
      
      结果:
      True
      False
      
    • 把可迭代对象转换为迭代器:iter函数
      # iter函数
      from collections import Iterable,Iterator
      s = "i love aksdjkajaks"
      
      print(isinstance(s,Iterable))
      print(isinstance(s,Iterator))
      
      # 使用iter函数
      s_i = iter(s)
      print(isinstance(s_i,Iterable))
      print(isinstance(s_i,Iterator))
      
      结果:
      True
      False
      True
      True
      

生成器

  • generator:一边循环一边计算下一个元素的机制/算法
  • 需要满足三个条件:
    • 每次调用都生产出for循环需要的下一个元素
    • 达到最后一个元素后,报出StopIteration异常
    • 可以被next函数调用
  • 生成一个生成器
    • 直接生成

      # 直接使用生成器
      
      # 放在中括号中是列表生成器
      l = [x*x for x in range(5)]  
      # 放在小括号中就是生成器
      g = (x*x for x in range(5))
      
      print(type(l))
      print(type(g))
      

      程序结果:

      <class 'list'>
      <class 'generator'>
      
    • 如果函数中包含yield语句,这个函数就是生成器。此类函数只能使用next调用,遇到yield就返回

      # 函数案例,此运行结果较简单
      def odd():
      	print("Step 1")
      	print("Step 2")
      	print("Step 3")
      	return None
      
      # 调用函数
      odd()
      

      把函数odd()变成生成器调用

      # 生成器案例
      # 在odd中,yield负责返回
      def odd():
          print("Step 1")
          yield 1
      
          print("Step 2")
          yield 2
      
          print("Step 3")
          yield 3
      
      # 生成一个生成器
      g = odd()
      one = next(g)
      print(one)
      
      # 第一次调用生成器后,在内部记住了上次运行后的位置,直接从语句print("Step 2")开始
      two = next(g)
      print(two)
      
      # 同上
      three = next(g)
      print(three)
      
    • for循环调用生成器(斐波纳契数列的写法)

      # 函数写法
      def fib(max):
          n,a,b = 0,0,1
          while n<max:
              print(b)
              a,b = b,a+b
              n += 1
          return "Done"
      
      fib(6)
      

      结果:

      1
      1
      2
      3
      5
      8
      # 注意,这里有return返回的值,只是没有打印出来
      
      # 生成器写法
      def fib(max):
          n,a,b = 0,0,1
          while n<max:
              yield b
              a,b = b,a+b
              n += 1
          return "Done"
      
      g = fib(6)
      for i in range(7):
          rst = next(g)
          print(rst)
      

      这种调用方法一定会报出StopIteration,结果如下:

      1
      1
      2
      3
      5
      8
      ---------------------------------------------------------------------------
      StopIteration                             Traceback (most recent call last)
      <ipython-input-4-b8fd53e94015> in <module>()
           10 g = fib(6)
           11 for i in range(7):
      ---> 12     rst = next(g)
           13     print(rst)
      
      StopIteration: Done
      
      
      # 上述代码改进
      def fib(max):
          n,a,b = 0,0,1
          while n<max:
              yield b
              a,b = b,a+b
              n += 1
          return "Done"
      
      """
      生成器的典型用法就是放在for中使用
      比较常用的典型生成器就是range()
      """
      g = fib(6)
      # 在for循环中使用生成器
      for i in g:
      	print(i)
      

协程

  • 历史进程
    • 3.4引入协程,用yield实现
    • 3.5引入协程语法
    • 实现协程比较好的包有asyncio,tornado,gevent
  • 定义:协程是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序
  • 从技术的角度讲,协程就是一个你可以暂停执行的函数,或者干脆就把协程理解成生成器
  • 实现:
    • yield返回
    • send调用
  • 优点:
    • 协程的切换相较于多线程很节约资源
	# 协程案例1
	def simple_coroutine():
	    print("-> start")
	    x = yield
	    print("-> recived",x)
	
	# 主线程
	sc = simple_coroutine()
	print(111)
	
	#可以使用sc.send(None),效果是一样的
	next(sc) #预激
	
	print(222)
	sc.send("hello")
结果:
	111
	-> start
	222
	-> recived hello
	---------------------------------------------------------------------------
	StopIteration                             Traceback (most recent call last)
	<ipython-input-8-756614beb6e0> in <module>()
	     13 
	     14 print(222)
	---> 15 sc.send("hello")
	
	StopIteration: 
  • 程序执行顺序如图:
    在这里插入图片描述
  • 协程的四个状态
    • inspect.getgeneratorstate(…) 函数确定,该函数会返回下述字符串中的一个:
    • GEN_CREATED:等待开始执行
    • GEN_RUNNING:解释器正在执行
    • GEN_SUSPENED:在yield表达式处暂停
    • GEN_CLOSE:执行结束
    • next 预激(prime)
# 协程案例2
def simple_coroutine():
	print("-> start")
	
	b = yield a
	print("-> recived",a,b)

	c = yield a + b
	print("-> recived",a,b,c)
# runc
sc = simple_corcoutine(5)

aa = next(sc)
print(aa)
bb = sc.send(6)
print(bb)
cc = sc.send(7)
print(cc)

代码运行结果:

-> start
5
-> recived 5,6
11
-> recived 5 6 7
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-8-756614beb6e0> in <module>()
	   16 bb = sc.send(6)
	   17 print(bb)
--->   18 cc = sc.send(7)
	   19 print(cc)
	   20

StopIteration: 

程序运行流程
在这里插入图片描述

  • 协程终止

    • 协程中未处理的异常会向上冒泡,传给next函数或send方法的调用方(即触发协程的对象)
    • 终止协程的一种方式:发送某个哨符值,让协程推出。内置的None和Ellipsis等常量经常用作哨符值。
  • yield from

    • 调用协程为了得到返回值,协程必须正常终止
    • 生成器正常终止会发出StopIteration异常,异常对象的value属性保存返回值
    • yield from从内部捕获StopIteration异常
    def gen():
    	for c in "AB":
    		yield c
    
    # list直接用生成器作为参数
    print(list(gen()))
    
    def gen_new():
    	yield from "AB"
    print(list(gen_new()))
    

    结果为:

    ['A','B']
    ['A','B']
    
  • 委派生成器
    - 包含yield from表达式的生成器函数
    - 委派生成器在yield from表达式处暂停, 调用方可以直接把数据发给子生成器
    - 子生成器再把产出的值发给调用方
    - 子生成器在最后,解释器会抛出StopIteration,并且把返回值附加到异常对象上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值