文章出處:http://m.survivalescaperooms.com/winstic/,請保留此連接
在python編程中,我們經常會看到函數中帶有yield關鍵字,但請注意,此時的函數不再是我們熟知的一般函數,而是所謂的generator(生成器)
生成器
對于生成器,可以對比于列表來看,我們在循環代碼中經常會使用range()產生一個list對象,繼而在for循環下依次遍歷,
for i in range(1000): PRint i
或者是使用列表生成式生成一個list對象:
[x * x for x in range(1000)]
這么做確實很方便,但這有個很大的缺點,我們所生產的list對象在程序運行過程中是存放在內存中的,占用內存大小與list規模有關,若要在編程時控制內存的占用,最好不要使用list。
相比于list對象對內存的占用,generator有很大的優勢,generator保存的是算法,不會生成所有的元素,而只是在調用next()時產生一個元素,很好的優化了內存占用的問題,可以通過next方法訪問數據,當沒有數據時會自動拋出StopIteration異常
>>> gener = (x * x for x in [1, 2, 3])>>> g = (x * x for x in [1, 2, 3])>>> g<generator object <genexpr> at 0x02534968>>>> g.next()1>>> g.next()4>>> g.next()9>>> g.next()Traceback (most recent call last): File "<pyshell#16>", line 1, in <module> g.next()StopIteration>>>
這么做的話難免有些繁瑣,還好在for循環中會幫我們實現next方法的調用也可以這么實現
>>> for i in g: print i
yield 初體驗
以上所實現的generator只是規律十分簡單的,這很好實現,只需要類似于列表生成式的簡單語法即可,那么對于其他的數列計算如何實現呢,例如斐波那契數列,它的定義雖然簡單:除第一、二個數據外,所有的數據均是其前兩個數據之和;如果我們通過一般函數實現,無疑當數列規模很大時,占用大量內存
>>> def fib(N): n, a, b = 0, 0, 1 while n < N: print b a, b = b, a + b n = n + 1
那么如何將上述方法轉換為generator加以實現呢,很簡單,只需要將print b 替換為yield b即可,我們可以試一下:
>>> def fib(N): n, a, b = 0, 0, 1 while n < N: yield b a, b = b, a + b n = n + 1 >>> fib(5)<generator object fib at 0x02530F80>>>> for i in fib(5): print i 11235
加了yield關鍵字后的函數是如何執行的呢,不應該說是函數,這時應該稱為generator;我們調用fib(5)并不會執行函數,而是返回一個generator對象,真正的執行是在調用next方法(for循環中自動調用next()),每次循環都會執行fib內的代碼,遇到yield則返回一個迭代值(類似于中斷);在下次循環時執行yield的下一語句,直至遇到下一個yield。
yield 協程
協程(coroutine)也叫微線程,相比于多線程更為高效,因為協程是多個程序在一個線程中執行,沒有線程間切換的開銷;同時在協程中不需要加鎖機制,因為在一個線程中不存在變量沖突問題。
例如經典問題(生產者-消費者問題)就可以使用協程機制實現,相比于多線程更為高效
def consumer(): r = '' while True: n = yield r if not n: return print 'consumer %s' % n r = 'OK'def produce(c): c.next() n = 0 while n < 5: n = n + 1 print 'produce %s' % n r = c.send(n) print 'consumer return %s' % r c.close()if __name__ == '__main__': c = consumer() produce(c)

produce 1consumer 1consumer return OKproduce 2consumer 2consumer return OKproduce 3consumer 3consumer return OKproduce 4consumer 4consumer return OKproduce 5consumer 5consumer return OK
在上述代碼中,consumer是一個生成器,執行過程中首先通過consumer產生generator對象c,
我們在執行到produce(c)的next方法時,才切換到生成器函數consumer中執行,
在consumer中遇到yield中斷,又切回到produce中
在produce中的c.send(n):主要干兩件事:1.將n添加到生成器中,2.返回下一個yield值(return next());所以當我們運行到send方法時,內含next機制進而切換到consumer函數中執行(傳入參數n),得到返回值'OK'(在下一個yield中返回)
。。。。。
最后在produce中關閉迭代器c.close()
總結:
新聞熱點
疑難解答