深入学习python的yield和generator

1160次阅读  |  发布于5年以前

前言
没有用过的东西,没有深刻理解的东西很难说自己会,而且被别人一问必然破绽百出。虽然之前有接触过python协程的概念,但是只是走马观花,这两天的一次交谈中,别人问到了协程,顿时语塞,死活想不起来曾经看过的东西,之后突然想到了yield,但为时已晚,只能说概念不清,所以本篇先缕缕python的生成器和yield关键字。

什么是生成器
1、生成器是一个特殊的程序,可以被用作控制循环的迭代行为
2、生成器类似于返回值为数组的一个函数,这个函数可以接收参数,可以被调用,但是,不同于一般的函数会一次性返回包含了所有数值的数组,生成器一次只产生一个值,这样消耗的内粗数量大大减少,而且允许调用函数可以很快的开始处理前几个返回值。因此,生成器看起来像一个函数但是表现的却像一个迭代器。
python中的生成器
python提供了两种基本的方式。

1)、生成器函数:也是用def来定义,利用关键字yield一次返回一个结果,阻塞,重新开始
2)、生成器表达式:返回一个对象,这个对象只有在需要的时候才产生结果
下面详细讲解。

1、生成器函数
为什么叫生成器函数?因为他随着时间的推移生成了一个数值队列。一般的函数在执行完毕之后会返回一个值然后退出,但是生成器函数会自动挂起,然后重新拾起继续执行,他会利用yield关键字关起函数,给调用者返回一个值,同时保留了当前的足够多的状态,可以使函数继续执行。生成器和迭代协议是密切相关的,可迭代的对象都有一个next()成员方法,这个方法要么返回迭代的下一项,要么引起异常结束迭代。
为了支持迭代协议,拥有yield语句的函数被编译为生成器,这类函数被调用时返回一个生成器对象,返回的对象支持迭代接口,即成员方法next()继续从中断处执行执行。
看下面的例子:


    # codes
    def create_counter(n):
     print "create counter"
     while True:
      yield n
      print 'increment n'
      n += 1

    cnt = create_counter(2)
    print cnt
    print next(cnt)
    print next(cnt)

    # output
    <generator object create_counter at 0x0000000001D141B0>
    create counter
    2
    increment n
    3

分析一下这个例子:

为了更加深刻的理解,我们再举一个例子。


    #coding
    def cube(n):
     for i in range(n):
      yield i ** 3

    for i in cube(5):
     print i

    #output
    0
    1
    8
    27
    64

所以从理解函数的角度出发我们可以将yield类比为return,但是功能确实完全不同,在for循环中,会自动遵循迭代规则,每次调用next()函数,所以上面的结果不难理解。

2、生成器表达式
生成器表达式来自于迭代和列表解析的组合,关于列表解析的概念和用法可以参见我之前的博客,生成器表达式和列表解析类似,但是他使用尖括号而不是方括号括起来的。如下代码:


    >>> # 列表解析生成列表
    >>> [ x ** 3 for x in range(5)]
    [0, 1, 8, 27, 64]
    >>> 
    >>> # 生成器表达式
    >>> (x ** 3 for x in range(5))
    <generator object <genexpr> at 0x000000000315F678>
    >>> # 两者之间转换
    >>> list(x ** 3 for x in range(5))
    [0, 1, 8, 27, 64]

就操作而言,生成器表如果使用大量的next()函数会显得十分不方便,for循环会自动出发next函数,所以可以按下面方式使用:


    >>> for n in (x ** 3 for x in range(5)):
     print('%s, %s' % (n, n * n))


    0, 0
    1, 1
    8, 64
    27, 729
    64, 4096
    >>> 

两者比较
一个迭代既可以被写成生成器函数,也可以被协程生成器表达式,均支持自动和手动迭代。而且这些生成器只支持一个active迭代,也就是说生成器的迭代器就是生成器本身。

总结
想起了初中时候老师经常说的,眼观千遍,不如手动一遍。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8