美文网首页
Learn Python 3: 生成器中的 yield 机制

Learn Python 3: 生成器中的 yield 机制

作者: SheHuan | 来源:发表于2019-03-09 22:14 被阅读0次

    开始前先认识下生成器,即generator

    创建列表可以使用列表生成式:

    >>>l = [v**2 for v in range(1,10)]
    >>>l
    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    但如果要创建的列表有百万甚至千万个元素,用到的元素只有前几个,考虑到内存的因素我们肯定不能直接创建,生成器就很好的解决了这个问题,生成器不会直接创建整个列表,而是在迭代的过程中推算出每个元素的值,是惰性的。

    把列表生成式的[]的换成()就是一个简单的生成器:

    >>>g = (v**2 for v in range(1,10))
    >>>g
    <generator object <genexpr> at 0x000000000220F1A8>
    

    要取出生成器每一个元素可以通过next()函数:

    0
    当没有元素可取时会抛出一个StopIteration异常,需要自行捕获处理。
    如果有成千上万个元素,一直next()当然不行了,由于生成器是可迭代的,可以用for循环来处理,还有一个好处就是不用考虑StopIteration异常:
    >>>for v in g:
    ...    print(n)
    

    可以使用isinstance()判断一个对象是否是可迭代对象(Iterable):

    >>>from collections import Iterable
    >>>isinstance(g, Iterable)
    True
    

    除了用()创建生成器,如果一个函数定义中有yield关键字,则这个函数也是一个生成器:

    def test():
        print('step-1')
        yield 1
        print('step-2')
        yield 2
        print('step-3')
        yield 3
    
    >>>g = test()
    >>>g
    <generator object test at 0x00000000027EF1A8>
    

    所以同样可以用next()或者for循环来迭代它。当每次执行next(g)时,生成器函数遇到yield表达式就会中断并返回yield表达式后边的参数值,例如:

    >>>next(g)
    step-1
    1
    

    再次执行时从上次返回的yield表达式处继续执行,返回下一个yield表达式的参数值,直到再无yield表达式参数可返回并抛出异常:

    >>>next(g)
    step-2
    2
    >>>next(g)
    step-3
    3
    

    除了next()外,还有一个很重要的函数send(),它和next()作用类似,但是send()可以发送值给对应的yield表达式,我们之前执行next(g)就相当于g.send(None)

    注意第一次调用next()send(None)相当于启动生成器,不能使用send()发送一个非None的值,否则会出错的(TypeError: can't send non-None value to a just-started generator),因为还没有yield表达式来接收这个值。

    那么启动生成器后,用send()发送的值如何被生成器中yield表达式接收呢?先修改上边的生成器函数:

    def test():
        print('step-1')
        x = yield 1
        print(x)
        print('step-2')
        y = yield 2
        print(y)
        print('step-3')
        x = yield 3
    

    我们打印了前两个yield表达式的值。接下来通过send()方式先启动生成器:

    >>>g = test()
    >>>g.send(None)
    step-1
    1
    

    仅仅是打印了提示语和返回了第一个yield表达式的参数值。再继续执行:

    >>>g.send('hello')
    hello
    step-2
    2
    

    可以看到先打印了用send()发送的值,然后是提示语和第二个yield表达式的参数值,所以这次send()发送的值被第一个yield表达式接收了,即yield 1表达式被赋值为hello,即x = 'hello'。再继续执行:

    >>>g.send('world')
    world
    step-3
    3
    

    即第二个yield 2表达式被赋值为world,并返回了第三个yield表达式的参数值。

    所以每次执行send()next()只是返回了对应yield表达式的参数值,其实对应表达式并未执行,直到下次再执行send()next()才会执行上次返回参数的yield表达式,所谓的执行yield表达式就是给其赋值,并返回下一个yield表达式的参数值!

    相关文章

      网友评论

          本文标题:Learn Python 3: 生成器中的 yield 机制

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