美文网首页
python 生成器和协程

python 生成器和协程

作者: 尛白兔 | 来源:发表于2017-09-16 16:43 被阅读14次

    yield

    对于python生成器中的yield来说,yield item会产出一个值,提供给next()的调用方,然后挂起生成器,直到再调用next()。

    def func():
        for i in range(10):
            yield i
    
    f = func()
    next(f) 
    

    协程

    在协程中,yield通常出现在表达式的右边(data = yield),可以返回一个值,也可以不返回(如果yield后面没有表达式,则返回None)。 生成器的调用方可以使用send()方法发送数据,发送的数据会成为yield表达式的值。因此,生成器可以作为协程使用。

    协程是一个过程,这个过程与调用方协作,产出由调用方提供的值

    >>> def func():
    ...     for i in range(10):
    ...             r = yield i    #遇到yield会暂时挂起生成器,并返回i
    ...             print(r)
    ... 
    >>> f = func()
    >>> next(f)     # 启动生成器才能向生成器发送数据
    0
    >>> f.send(10)
    10
    1
    

    *** 在使用f.send()之前,一定要先预激协程 ***

    为了简化协程的用法,可以使用一个预激装饰器

    import functools
    def wrapper(func):
        @functools.wraps(func)    # 使用wraps是为了保留func的函数名
        def inner(*args, **kwargs):
            gen = func(*args, **kwargs)
            next(gen)
            return gen
        return inner
    
    
    def func():
        for i in range(10):
            r = yield i
            print(r)
    

    终止协程和异常处理

    如果协程内没有处理异常, 协程会终止,再次调用协程,会抛出StopIteration异常

    >>> f = func()
    >>> f.send(10)
    100
    1
    >>> f.send('hello')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in func
    TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
    >>> f.send(10)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
    

    让协程返回值

    在协程中使用return返回的值会放在StopIteration异常中的value变量中,捕获这个异常,然后获取即可

    yield from

    使用yield from 会自动捕获StopIteration异常,还会把value变量的值变成yield from 表达式的值。

    def func():
        for i in range(10):
            r = yield i
        
    yield from rang(10) 等同于func()
    

    yield from 将调用方法与内层的子生成器连接起来, 外层的调用方法可以向内层的生成器直接传递值,子生成器返回的值由yield from接收.
    下面看一个例子

    #!/usr/bin/env python3
    from collections import namedtuple
    
    Result = namedtuple('Result', 'count average')# 子生成器
    
    # 这个例子和上边示例中的 averager 协程一样,只不过这里是作为字生成器使用
    
    def averager():
        total = 0.0
        count = 0
        average = None
        while True:        # main 函数发送数据到这里 
            term = yield
            if term is None: # 终止条件
                break
            total += term
            count += 1
            average = total/count
        return Result(count, average) # 返回的Result 会成为grouper函数中yield from表达式的值
    
    
    # 委派生成器
    def grouper(results, key):
        # 这个循环每次都会新建一个averager 实例,每个实例都是作为协程使用的生成器对象
        while True:        # grouper 发送的每个值都会经由yield from 处理,通过管道传给averager 实例。grouper会在yield from表达式处暂停,等待averager实例处理客户端发来的值。averager实例运行完毕后,返回的值绑定到results[key] 上。while 循环会不断创建averager实例,处理更多的值。
            results[key] = yield from averager()
        
    # 调用方
    def main(data):
        results = {}
        for key, values in data.items():
            # group 是调用grouper函数得到的生成器对象,传给grouper 函数的第一个参数是results,用于收集结果;第二个是某个键
            group = grouper(results, key)
            next(group)        
            for value in values:            # 把各个value传给grouper 传入的值最终到达averager函数中;
                # grouper并不知道传入的是什么,同时grouper实例在yield from处暂停
                group.send(value)        # 把None传入groupper,传入的值最终到达averager函数中,导致当前实例终止。然后继续创建下一个实例。
            # 如果没有group.send(None),那么averager子生成器永远不会终止,委派生成器也永远不会在此激活,也就不会为result[key]赋值
            group.send(None)
        report(results)
            
    # 输出报告
    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, 41, 42, 43, 44, 54],
        'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],    
        'boys;kg':[50, 51, 62, 53, 54, 54],    
        'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
    }
    
    if __name__ == '__main__':
        main(data)
    

    相关文章

      网友评论

          本文标题:python 生成器和协程

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