美文网首页
学Python,不明白协程那就亏大了

学Python,不明白协程那就亏大了

作者: 楼上那位 | 来源:发表于2017-12-02 22:32 被阅读61次

学习Python很多书籍都或多或少介绍协程,但是很少有人会在开发中使用,一是理解不深,使用怕出错,二是有其他方式可以替代,知识代码多一点,便于理解啊,总之:不使用的理由千千种。
今天我就来和大家聊聊Python协程

  1. 协程是啥

  2. 协程在实际的应用

协程的定义

协程 是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序”。从技术的角度来说,“协程就是你可以暂停执行的函数”

与生成器类似,但yield 出现在表达式的右边,且产出一个表达式值作为返回值,如果yield后没有表达式,则返回None。协程可以从调用方接收数据,caller.send(params)。

1. 例子
>>> def simple(a):
    print "start:a = ", (a)
    b = yield a
    print "received b= ",(b)
    c = yield a + b
    print "received :d ", (c)

    
>>> m1 = simple(2)
>>> m1
<generator object simple at 0x0000000003D830D8>
>>> next(m1) # 激活协程, 并产出a,然后暂停
start:a =  2
2
>>> m1.send(3)
received b=  3
5
>>> m1.send(1000) # b 接收数据,并产出 a+b 的结果,然后暂停 
received :d  1000

Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    m1.send(1000)
StopIteration

yield作为控制流程的方式来理解。

image.png

上图中 第一阶段 yield a结束,第二阶段是给b 赋值开始

再看一个例子:

def averager():
    total = 0
    count = 0
    avg = None
    while True:
        term = yield avg 
        total += term
        count +=1
        avg = total/count

ag = averager()
next(ag) # 运行到 yield avg 就停止,并返回avg值 None
ag.send(100) # item 接收到100,并运行,直到下一个yield 并停止

2. 与协程相关的方法

我们知道了怎么创建协程,但是当执行到最后,如果不处理就会和生成器一样抛出一个stopInteration异常来终止该协程,从2.5版本开始添加了两个方法throwclose来终止协程

如果generator.thow(exception)的异常被捕获,则继续下一个yield,否则终止

3. 协程返回值

在定义体中 return value

from collections import namedTuple
Result = namedTuple("Result","count average")

def average():
      total = 0
      count = 0
      avg = None
      while True:
             term = yield
             if term is None:
                break   # 终止符
             total +=term
             count +=1
             avg = total/count
     return Result(count,avg)

send(None) 就会break, 返回。其中Result 会作为异常的value属性返回

try:
     m2 .send(None)
exception StopInteration as exc:
     print exc.value

应用



# BEGIN YIELD_FROM_AVERAGER
from collections import namedtuple

Result = namedtuple('Result', 'count average')


# the subgenerator
def averager():  # <1>
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # <2>
        if term is None:  # <3>
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)  # <4>


# the delegating generator
def grouper(results, key):  # <5>
    while True:  # <6>
        results[key] = yield from averager()  #生成一个使用协程的生成器,在此处暂停,等 将一个Result 赋值给对应的key


# the client code, a.k.a. the caller
def main(data):  # <8>
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # group是grouper函数生成的生成器对象
        next(group)  # 预激活协程
        for value in values:
# 把各个 value 传给 grouper。传入的值最终到达 averager 函数中 term = yield 那一行; grouper 永远不知道传入的值是什么
            group.send(value)  

# 内层循环结束后, group 实例依旧在 yield from 表达式处暂停,因此, grouper函数定义体中为 results[key] 赋值的语句还没有执行

#把 None 传入 grouper,导致当前的 averager 实例终止,也让 grouper 继续运行,再创建一个 averager 实例,处理下一组值
        group.send(None) 

    # print(results)  # uncomment to debug
    report(results)


# output report
def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(';')
        print('{:2} {:5} averaging {:.2f}{}'.format(
              result.count, group, result.average, unit))


data = {
    'girls;kg':
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    'girls;m':
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    'boys;kg':
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    'boys;m':
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}


if __name__ == '__main__':
    main(data)

上述例子,如果子生成器不停止,委派生成器永远在yield from 处停止。

yield from iterable本质上等于for item in iterable: yield item的缩写

yield from的意义

把迭代器当作生成器使用,相当于把子生成器的定义体内联在 yield from 表达式
中。此外,子生成器可以执行 return 语句,返回一个值,而返回的值会成为 yield
from 表达式的值

  1. 子生成器产出的值都直接传给委派生成器的调用方(即客户端代码将上述例子的averager 直接给了main中的group)。

  2. 使用 send() 方法发给委派生成器的值都直接传给子生成器。如果发送的值是
    None,那么会调用子生成器的 __next__()方法。如果发送的值不是None,那么会
    调用子生成器的 send()方法。如果调用的方法抛出 StopIteration 异常,那么委
    派生成器恢复运行。任何其他异常都会向上冒泡,传给委派生成器。

  3. 生成器退出时,生成器(或子生成器)中的return expr表达式会触发
    StopIteration(expr)异常抛出。

  4. yield from 表达式的值是子生成器终止时传给 StopIteration 异常的第一个参
    数。

** yield from 结构的另外两个特性与异常和终止有关。**

  • 传入委派生成器的异常,除了GeneratorExit之外都传给子生成器的throw()
    法。如果调用 throw()方法时抛出StopIteration 异常,委派生成器恢复运
    行。 StopIteration 之外的异常会向上冒泡,传给委派生成器。
  • 如果把GeneratorExit 异常传入委派生成器,或者在委派生成器上调用close()
    法,那么在子生成器上调用close()方法,如果它有的话。如果调用close() 方法
    导致异常抛出,那么异常会向上冒泡,传给委派生成器;否则,委派生成器抛出
    GeneratorExit异常。

相关文章

  • 学Python,不明白协程那就亏大了

    学习Python很多书籍都或多或少介绍协程,但是很少有人会在开发中使用,一是理解不深,使用怕出错,二是有其他方式可...

  • python异步协程(aiohttp,asyncio)

    python异步协程 环境:python3.7.0 协程 协程,英文叫做 Coroutine,又称微线程,纤程,协...

  • Python 协程的基本概念

    Python 协程的基本概念 在学习 Python 基础的过程中,遇到了比较难理解的地方,那就是协程。刚开始看了廖...

  • Python编程之从生成器使用入门协程

    从今天开始,我们将开始进入Python的难点,那就是协程。 为了写明白协程的知识点,我查阅了网上的很多相关资料。发...

  • asyncio + asyncio 异步编程实例

    协程用法 接下来,我们来了解下协程的实现,从 Python 3.4 开始,Python 中加入了协程的概念,但这个...

  • Python 协程

    仅供学习,转载请注明出处 协程 协程,又称微线程,纤程。英文名Coroutine。 协程是啥 协程是python个...

  • 协程

    1.协程 协程,又称微线程,纤程。英文名Coroutine。 1.1 协程是什么 协程是python个中另外一种实...

  • Python并发编程——协程

    摘要:Python,协程,gevent 协程基本概念 协程,又称微线程,纤程。英文名Coroutine,是Pyth...

  • 协程介绍

    协程 协程,又称微线程,纤程。英文名Coroutine。 1.协程是什么? 协程是python个中另外一种实现多任...

  • 4-7

    协程 协程,又称微线程,纤程。英文名Coroutine。 协程是啥 协程是python个中另外一种实现多任务的方式...

网友评论

      本文标题:学Python,不明白协程那就亏大了

      本文链接:https://www.haomeiwen.com/subject/wsbnbxtx.html