Python之美[從菜鳥到高手]–產生器之全景分析

來源:互聯網
上載者:User

    yield指令,可以暫停一個函數並返回中間結果。使用該指令的函數將儲存執行環境,並且在必要時恢複。

產生器比迭代器更加強大也更加複雜,需要花點功夫好好理解貫通。

看下面一段代碼:

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

     只要函數中包含yield關鍵字,該函數調用就是產生器對象。

g=gen()print g   #<generator object gen at 0x02801760>print isinstance(g,types.GeneratorType) #True

    我們可以看到,gen()並不是函數調用,而是產生產生器對象。

   產生器對象支援幾個方法,如gen.next() ,gen.send() ,gen.throw()等。

print g.next() # 0

    調用產生器的next方法,將運行到yield位置,此時暫停執行環境,並返回yield後的值。所以列印出的是1,暫停執行環境。

print g.next() #None  1

     再調用next方法,你也許會好奇,為啥列印出兩個值,不急,且聽我慢慢道來。

     上一次調用next,執行到yield 0暫停,再次執行恢複環境,給tmp賦值(注意:這裡的tmp的值並不是x的值,而是通過send方法接受的值),由於我們沒有調用send方法,所以

tmp的值為None,此時輸出None,並執行到下一次yield x,所以又輸出1.

      到了這裡,next方法我們都懂了,下面看看send方法。

print g.send('hello') #world  2

      上一次執行到yield 1後暫停,此時我們send('hello'),那麼程式將收到‘hello',並給tmp賦值為’hello',此時tmp=='hello'為真,所以輸出'world',並執行到下一次yield 2,所以又列印出2.(next()等價於send(None))

      當迴圈結束,將拋出StopIteration停止產生器。

      看下面代碼:

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

正如你所預料的,列印出’nono',由於沒有額外的yield,所以將直接拋出StopIteration。

nonoTraceback (most recent call last):  File "F:\python workspace\Pytest\src\cs.py", line 170, in <module>    s.next()StopIteration

      看下面代碼,理解throw方法,throw主要是向產生器發送異常。

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  將會捕捉,並輸出資訊。

     理解了這些,我們就可以向協同程式發起攻擊了,所謂協同程式也就是是可以掛起,恢複,有多個進入點。其實說白了,也就是說多個函數可以同時進行,可以相互之間發送訊息等。

     這裡有必要說一下multitask模組(不是標準庫中的),看一段multitask使用的簡單代碼:

def tt():    for x in xrange(4):        print 'tt'+str(x)        yielddef gg():    for x in xrange(4):        print 'xx'+str(x)        yieldt=multitask.TaskManager()t.add(tt())t.add(gg())t.run()

結果:

tt0xx0tt1xx1tt2xx2tt3xx3

   如果不是使用產生器,那麼要實現上面現象,即函數交錯輸出,那麼只能使用線程了,所以產生器給我們提供了更廣闊的前景。 

   如果僅僅是實現上面的效果,其實很簡單,我們可以自己寫一個。主要思路就是將產生器對象放入隊列,執行send(None)後,如果沒有拋出StopIteration,將該產生器對象再排入佇列。

class Task():    def __init__(self):        self._queue = Queue.Queue()    def add(self,gen):        self._queue.put(gen)    def run(self):        while not self._queue.empty():            for i in xrange(self._queue.qsize()):                try:                    gen= self._queue.get()                    gen.send(None)                except StopIteration:                    pass                else:                    self._queue.put(gen)t=Task()t.add(tt())t.add(gg())t.run()

  當然,multitask實現的肯定不止這個功能,有興趣的童鞋可以看下源碼,還是比較簡單易懂的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.