最近有很多的同学问,大家都知道多线程,多进程,那么这个协程有什么什么东西?难不成还是携程旅游(此处没有广告费)?能不能说一下Python协程,而且最好要讲清楚!

那行,今天将来讲解一下Python协程

前言

从概念上来说,我们都知道多进程和多线程,而协程其实是在单线程中实现多并发。从句法上看,协程与生成器类似,都是定义体中包含yield关键字的函数。区别在于协程的yield通常出现在表达式的右边:​​datum = yield​​。这一下就让初学者瞬间觉得yield关键字不香了,本来以为yield就是简简单单的暂停执行顺手返回个值,结果还能放右边?

从生成器到协程

先看一个可能是协程最简单的使用示例:

>>> def simple_coroutine():
...     print("-> coroutine started")
...     x = yield
...     print("-> coroutine received:", x)
...
>>> my_coro = simple_coroutine()
>>> my_coro
<generator object simple_coroutine at 0x0000019A681F27B0>
>>> next(my_coro)
-> coroutine started
>>> my_coro.send(42)
-> coroutine received: 42
Traceback (most recent call last):File "<input>", line 1, in <module>
StopIteration

之所以yield可以放右边,是因为协程可以接收调用方使用​​.send()​​推送的值。

yield放在右边以后,它的右边还能再放个表达式,请看下面这个例子:

def simple_coro2(a):b = yield ac = yield a + bmy_coro2 = simple_coro2(14)
next(my_coro2)
my_coro2.send(28)
my_coro2.send(99)

执行过程是:
    调用next(my_coro2),执行yield a,产出14。
    调用my_coro2.send(28),把28赋值给b,然后执行yield a + b,产出42。
    调用my_coro2.send(99),把99赋值给c,协程终止。

由此得出结论,对于b = yield a这行代码来说,= 右边的代码在赋值之前执行。

在示例中,需要先调用​​next(my_coro)​​启动生成器,让程序在yield语句处暂停,然后才可以发送数据。这是因为协程有四种状态:

  • 'GEN_CREATED' 等待开始执行
  • 'GEN_RUNNING' 解释器正在执行
  • 'GEN_SUSPENDED' 在yield表达式处暂停
  • 'GEN_CLOSED' 执行结束

只有在GEN_SUSPENDED状态才能发送数据,提前做的这一步叫做预激,既可以调用​​next(my_coro)​​​预激,也可以调用​​my_coro.send(None)​​预激,效果一样。

预激协程

协程必须预激才能使用,也就是send前,先调用next,让协程处于GEN_SUSPENDED状态。但是这件事经常会忘记。为了避免忘记,可以定义一个预激装饰器,比如:

from functools import wrapsdef coroutine(func):@wraps(func)def primer(*args, **kwargs):gen = func(*args, **kwargs)next(gen)return genreturn primer

但实际上Python给出了一个更优雅的方式,叫做yield from,它会自动预激协程。

自定义预激装饰器和yield from是不兼容的。

yield from

yield from相当于其他语言中的await关键字,作用是:在生成器gen中使用yield from subgen()时,subgen会获得控制权,把产出的值传给gen的调用方,即调用方可以直接控制subgen。与此同时,gen会阻塞,等待subgen终止。

yield from可以用来简化for循环中的yield:

for c in "AB":yield c
yield from "AB"

yield from x表达式对x做的第一件事就是,调用iter(x),从中获取迭代器。

但yield from的作用远不止于此,它更重要的作用是打开双向通道。如下图所示:

这个图信息量很大,很难理解。

首先要理解这3个概念:调用方、委派生成器、子生成器。

  • 调用方
    说白了就是main函数,也就是众所周知的程序入口main函数

    # the client code, a.k.a. the caller def main(data): # <8> results = {} for key, values in data.items(): group = grouper(results, key) # <9> next(group) # <10> for value in values: group.send(value) # <11> group.send(None) # important! <12> # print(results) # uncomment to debug report(results) ​
  • 委派生成器
    就是包含了yield from语句的函数,也就是协程

    # the delegating generator def grouper(results, key): # <5> while True: # <6> results[key] = yield from averager() # <7> ​

这比术语看着舒服多了。

然后是5条线:send、yield、throw、StopIteration、close。

  • send    
       协程在yield from表达式处暂停时,main函数可以通过yield from表达式把数据发给yield from语句右边跟着的子协程。
  • yield
        yield from语句右边跟着的子协程再把产出的值通过yield from表达式发给main函数。
  • hrow
        main函数通过​​group.send(None)​​,传入一个None值,让yield from语句右边跟着的子协程的while循环终止,这样控制权才会交回协程,才能继续执行,否则会一直暂在yield from语句暂停。
  • StopIteration
        yield from语句右边跟着的生成器函数返回之后,解释器会抛出StopIteration异常。并把返回值附加到异常对象上,此时协程会恢复。
  • close
        main函数执行完以后,会调用close()方法退出协程。

大体流程搞清楚了,更多的技术细节就不继续研究了,有时间的话,在以后的Python原理系列中再学习吧。

yield from经常与Python3.4标准库里的​​@asyncio.coroutine​​装饰器结合使用。

协程用作累加器

这是协程的常见用途,代码如下:

def averager():total = 0.0count = 0average = Nonewhile True:  # <1>term = yield average  # <2>total += termcount += 1average = total/count

协程实现并发

这里例子有点复杂​

核心代码片段是:

# BEGIN TAXI_PROCESS
def taxi_process(ident, trips, start_time=0):  # <1>"""Yield to simulator issuing event at each state change"""time = yield Event(start_time, ident, 'leave garage')  # <2>for i in range(trips):  # <3>time = yield Event(time, ident, 'pick up passenger')  # <4>time = yield Event(time, ident, 'drop off passenger')  # <5>yield Event(time, ident, 'going home')  # <6># end of taxi process # <7>
# END TAXI_PROCESS
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS,seed=None):"""Initialize random generator, build procs and run simulation"""if seed is not None:random.seed(seed)  # get reproducible resultstaxis = {i: taxi_process(i, (i+1)*2, i*DEPARTURE_INTERVAL)for i in range(num_taxis)}sim = Simulator(taxis)sim.run(end_time)

这个示例说明了如何在一个主循环中处理事件,以及如何通过发送数据驱动协程。这是asyncio包底层的基本思想。使用协程代替线程和回调,实现并发。

教完收工。

罗师傅镇楼

上面五要是都看懂了的话,你对Python协程这块也算是比较熟了,恭喜!!!


ok我话说完了

夜来风雨声,Python协程知多少相关推荐

  1. 简单聊聊Python协程

    往期好文推荐 学习Python不需要程基础? 0基础不用怕,从0到1轻松教你入门Python python系统学习流线图,教你一步一步学会python 成为一名做大数据开发的女程序员,并不是二狗进入大 ...

  2. python 协程_Python 协程与 Go 协程的区别(一)

    ? "Python猫" ,一个值得加星标的公众号 花下猫语:年关将近,不知各位过得怎样?我最近有些忙,收获也挺多,以后有机会分享下.吃饭时间,追了两部剧<了不起的麦瑟尔夫人& ...

  3. python协程实时输出_python协程

    不知道你有没有被问到过有没有使用过的python协程? 协程是什么? 协程是一种用户态轻量级,是实现并发编程的一种方式.说到并发,就能想到了多线程 / 多进程模型,是解决并发问题的经典模型之一. 但是 ...

  4. python中协程与函数的区别_深入浅析python 协程与go协程的区别

    进程.线程和协程 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定义: 操作系统能够进行运算调度的最小单位.它被包含在进 ...

  5. python 协程可以嵌套协程吗_Python线程、协程探究(2)——揭开协程的神秘面纱...

    一.上集回顾 在上一篇中我们主要研究了python的多线程困境,发现多核情况下由于GIL的存在,python的多线程程序无法发挥多线程该有的并行威力.在文章的结尾,我们提出如下需求: 既然python ...

  6. c++ 协程_理解Python协程(Coroutine)

    由于GIL的存在,导致Python多线程性能甚至比单线程更糟. GIL: 全局解释器锁(英语:Global Interpreter Lock,缩写GIL),是计算机程序设计语言解释器用于同步线程的一种 ...

  7. python 协程原理_Python协程greenlet实现原理

    greenlet是stackless Python中剥离出来的一个项目,可以作为官方CPython的一个扩展来使用,从而支持Python协程.gevent正是基于greenlet实现. 协程实现原理 ...

  8. python 协程 php,python3.x,协程_python协程练习部分代码的理解?,python3.x,协程,asyncio - phpStudy...

    python协程练习部分代码的理解? import asyncio import threading async def wget(host): print('wget {}'.format(host ...

  9. 从yield 到yield from再到python协程

    yield 关键字 def fib():a, b = 0, 1while 1:yield ba, b = b, a+b yield 是在:PEP 255 -- Simple Generators 这个 ...

最新文章

  1. HDU 4628 Pieces(DP + 状态压缩)
  2. IE兼容问题 动态生成的节点IE浏览器无法触发
  3. springboot 集成mybatis_Spring Boot 集成Mybatis实现多数据源
  4. pycache怎么用_如何用python3输出dos命令?
  5. VMware、vSphere 6.0 介绍
  6. c性能大容量cket_水合低共熔电解质有助于改善水性锌电池的性能
  7. flutter压缩图片上传
  8. 第八章:Java8新增特性,Lambda表达式
  9. 使用uniapp开发微信小程序的人脸采集功能/人脸识别功能
  10. itext生成pdf间距_[itext]Java生成PDF文件
  11. AWS 中文入门开发教学 21- 通过 Web 应用连接到 MySQL 数据库服务器
  12. 第十一届蓝桥杯 ——互质
  13. Android 开发技术干货
  14. 神经网络学说的主要观点,对神经网络的简单理解
  15. excel培训机构_毕马威股权投资估值与税务内部培训(2019)52页
  16. 在合约中获取代币余额のEOS智能合约系列
  17. Number Triangles 数字金字塔
  18. H5互动小游戏定制开发注意事项
  19. 阿里云VOD 视频点播(二)、VUE视频上传,视频播放
  20. js文件上传精准校验文件类型,防止直接.mp4改成.mp3上传

热门文章

  1. Microsoft Windows Workflow Foundation Step by Step Charpter 2
  2. 双人零和博弈(two player zero-sum game)的性质
  3. NDK_PROJECT_PATH=null
  4. direct wifi 投屏_【教程】Microsoft Wi-Fi Direct Virtual关闭方法
  5. Java虚拟机学习资料整理
  6. 解决ROS包校验不符问题,国内易科ros源
  7. android glide流程解析,Glide 源码解析(一):简单流程分析
  8. 聊聊 effects 与 reducers--React AntDesign Dva
  9. 【转】“上善若水”是什么意思?
  10. 2008521美赛E题