• Python生成器和协程


    生成器

    生成器对象是迭代器对象,更是可迭代对象,它实现了__iter____next__协议

    Python通过在函数中使用yield关键字来定义一个生成器工厂函数,当工厂函数调用时生成一个生成器对象

    def gen(a):
        print('Started...')
        yield a
        yield a + 10
        print('End...')
    

    yield关键字有两个作用:

    • 产出, 当使用next(Generator)内置函数它会产出yield expr语句中expr的值
    • 让步, 程序执行完yield右侧的表达式后会暂停执行并将控制权交给调用方

    当生成器运行到定义体的结尾会抛出StopIteration异常。如果函数体内部有return expr语句,当执行此语句时会抛出StopIteration异常并结束此生成器,同时return expr语句中的expr会被赋值给StopIteration.value

    # 这是执行return语句时终端打印的信息,expr即是return的返回值
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration: expr
    

    for循环语句中迭代生成器对象,不会抛出异常是因为Python语言内部会处理for循环和其他迭代上下文(如列表推导、元组拆包等等)中的StopIteration异常

    协程

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

    通过在生成器函数内部使用Received = yield expr表达式让普通的生成器进化为协程。调用方通过生成器的send方法发送消息,并返回yield产出的值

    def coro(a):
        print('-> Started: a =', a)
        b = yield a
        print('-> Received: b =', b)
        c = yield a + b
        print('-> Received: c =', c)
    
    if __name__ == '__main__':
        coroObject = coro(14)
        next(coroObject)
        coroObject.send(28)
        coroObject.send(99)
    

    使用协程,第一步必须使用next内置函数预激协程。

    send(value)方法将value值赋给Received = yield expr表达式中的Received而不是expr,该方法的返回值是expr的值。协程之所以要预激,是因为send方法赋值需要程序先运行到=,进行赋值

    yield from语法

    def subGen():
        for i in range(5):
            rece = yield i
            if rece is None:
                break
        return 'end'
    
    def gen():
        while True:
            result = yield from subGen()
            print(result)
    

    yield from的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码.

    调用方发送的值传递给子生成器并阻塞委派生成器,子生成器只有执行return语句才会结束阻塞并将return语句的返回值赋给received

    yield from语法的意义是:

    • 协程需要预激,yield from会预激子生成器
    • 处理子生成器运行结束时抛出的StopIteration异常
    • 处理子生成器执行return语句抛出的StopIteration异常,并把return语句的返回值绑定给yield from表达式左侧变量
    • 建立双向通道,调用方调用委派生成器的send方法时,发送值传递给子生成器Received = yield exprReceived变量中;同时将子生成器yield <expr>中的expr作为委派生成器send方法的返回值

    委派生成器本身也是生成器,当委派生成器结束时也会抛出StopIteration异常。所以一般在委派生成器中使用循环,避免抛出异常

  • 相关阅读:
    24. Swap Nodes in Pairs
    49. Group Anagrams
    280. Wiggle Sort
    274. H-Index
    K Closest Numbers In Sorted Array
    Closest Number in Sorted Array
    Last Position of Target
    Classical Binary Search
    350. Intersection of Two Arrays II
    Sort Integers II
  • 原文地址:https://www.cnblogs.com/weixia-blog/p/12945322.html
Copyright © 2020-2023  润新知