Python之美[从菜鸟到高手]–生成器之全景分析

 yield指令,可以暂停一个函数并返回中间结果。使用该指令的函数将保存执行环境,并且在必要时恢复。

生成器比迭代器更加强大也更加复杂,需要花点功夫好好理解贯通。

看下面一段代码:

1234567 def gen():    for x in xrange(4):        tmp = yield x        if tmp == \’hello\’:            print \’world\’        else:            print str(tmp)

只要函数中包含yield关键字,该函数调用就是生成器对象。

123 g=gen()print g   #print isinstance(g,types.GeneratorType) #True

我们可以看到,gen()并不是函数调用,而是产生生成器对象。

生成器对象支持几个方法,如gen.next() ,gen.send() ,gen.throw()等。

1 print g.next() # 0

调用生成器的next方法,将运行到yield位置,此时暂停执行环境,并返回yield后的值。所以打印出的是0,暂停执行环境。

1 print g.next() #None  1

再调用next方法,你也许会好奇,为啥打印出两个值,不急,且听我慢慢道来。

上一次调用next,执行到yield 0暂停,再次执行恢复环境,给tmp赋值(注意:这里的tmp的值并不是x的值,而是通过send方法接受的值),由于我们没有调用send方法,所以

tmp的值为None,此时输出None,并执行到下一次yield x,所以又输出1.

到了这里,next方法我们都懂了,下面看看send方法。

1 print g.send(\’hello\’) #world  2

上一次执行到yield 1后暂停,此时我们send(‘hello’),那么程序将收到‘hello’,并给tmp赋值为’hello’,此时tmp==’hello’为真,所以输出’world’,并执行到下一次yield 2,所以又打印出2.(next()等价于send(None))

当循环结束,将抛出StopIteration停止生成器。

看下面代码:

12345678 def stop_immediately(name):    if name == \’skycrab\’:        yield \’okok\’    else:        print \’nono\’ s=stop_immediately(\’sky\’)s.next()

正如你所预料的,打印出’nono’,由于没有额外的yield,所以将直接抛出StopIteration。

12345 nonoTraceback (most recent call last):  File \”F:python workspacePytestsrccs.py\”, line 170, in     s.next()StopIteration

看下面代码,理解throw方法,throw主要是向生成器发送异常。

12345678910 def mygen():    try:        yield \’something\’    except ValueError:        yield \’value error\’    finally:        print \’clean\’  #一定会被执行gg=mygen()print gg.next() #somethingprint gg.throw(ValueError) #value error  clean

调用gg.next很明显此时输出‘something’,并在yield ‘something’暂停,此时向gg发送ValueError异常,恢29739359\” class=\”crayon-syntax crayon-theme-github crayon-font-monaco crayon-os-pc print-yes notranslate\” data-settings=\” minimize scroll-always\” style=\” margin-top: 12px; margin-bottom: 12px; font-size: 13px !important; line-height: 15px !important;\”>

1234567 def gen():    for x in xrange(4):        tmp = yield x        if tmp == \’hello\’:            print \’world\’        else:            print str(tmp)

只要函数中包含yield关键字,该函数调用就是生成器对象。

123 g=gen()print g   #print isinstance(g,types.GeneratorType) #True

我们可以看到,gen()并不是函数调用,而是产生生成器对象。

生成器对象支持几个方法,如gen.next() ,gen.send() ,gen.throw()等。

1 print g.next() # 0

调用生成器的next方法,将运行到yield位置,此时暂停执行环境,并返回yield后的值。所以打印出的是0,暂停执行环境。

1 print g.next() #None  1

再调用next方法,你也许会好奇,为啥打印出两个值,不急,且听我慢慢道来。

上一次调用next,执行到yield 0暂停,再次执行恢复环境,给tmp赋值(注意:这里的tmp的值并不是x的值,而是通过send方法接受的值),由于我们没有调用send方法,所以

tmp的值为None,此时输出None,并执行到下一次yield x,所以又输出1.

到了这里,next方法我们都懂了,下面看看send方法。

1 print g.send(\’hello\’) #world  2

上一次执行到yield 1后暂停,此时我们send(‘hello’),那么程序将收到‘hello’,并给tmp赋值为’hello’,此时tmp==’hello’为真,所以输出’world’,并执行到下一次yield 2,所以又打印出2.(next()等价于send(None))

当循环结束,将抛出StopIteration停止生成器。

看下面代码:

12345678 def stop_immediately(name):    if name == \’skycrab\’:        yield \’okok\’    else:        print \’nono\’ s=stop_immediately(\’sky\’)s.next()

正如你所预料的,打印出’nono’,由于没有额外的yield,所以将直接抛出StopIteration。

12345 nonoTraceback (most recent call last):  File \”F:python workspacePytestsrccs.py\”, line 170, in     s.next()StopIteration

看下面代码,理解throw方法,throw主要是向生成器发送异常。

12345678910 def mygen():    try:        yield \’something\’    except ValueError:        yield \’value error\’    finally:        print \’clean\’  #一定会被执行gg=mygen()print gg.next() #somethingprint gg.throw(ValueError) #value error  clean

调用gg.next很明显此时输出‘something’,并在yield ‘something’暂停,此时向gg发送ValueError异常,恢č执行环境,except  将会捕捉,并输出信息。

理解了这些,我们就可以向协同程序发起攻击了,所谓协同程序也就是是可以挂起,恢复,有多个进入点。其实说白了,也就是说多个函数可以同时进行,可以相互之间发送消息等。