理解yield(生成器)
任何使用yield關鍵字的函式都稱之為生成器,如:
def count(n): while n > 0: yield n #生成值:n n -= 1
另外一種說法:生成器就是一個返回迭代器的函式,與普通函式的區別是生成器包含yield語句,更簡單點理解生成器就是一個迭代器。(有關迭代器和生成器的區別可以看文章末尾處補充)
使用yield,可以讓函式生成一個序列,該函式返回的物件型別是”generator”,通過該物件連續呼叫next()方法返回序列值。
c = count(5) c.next() >>> 5 c.next() >>>4
生成器函式只有在呼叫next()方法的時候才開始執行函式裡面的語句,比如:
def count(n): print "cunting" while n > 0: yield n #生成值:n n -= 1
在呼叫count函式時:c=count(5),並不會列印”counting”只有等到呼叫c.next()時才真正執行裡面的語句。每次呼叫next()方法時,count函式會執行到語句yield n處為止,next()的返回值就是生成值n,再次呼叫next()方法時,函式繼續執行yield之後的語句(熟悉Java的朋友肯定知道Thread.yield()方法,作用是暫停當前執行緒的執行,讓其他執行緒執行。python中的協程和yield的有著千絲萬縷的聯絡),如:
def count(n): print "cunting" while n > 0: print 'before yield' yield n #生成值:n n -= 1 print 'after yield'
上述程式碼在第一次呼叫next方法時,並不會列印”after yield”。如果一直呼叫next方法,當執行到沒有可迭代的值後,程式就會報錯:
Traceback (most recent call last): File “”, line 1, in StopIteration 所以一般不會手動的呼叫next方法,而使用for迴圈:
for i in count(5): print i,
例項:
用yield生成器模擬Linux中命令:tail -f | grep python?用於查詢監控日誌檔案中出現有python字樣的行。
import time def tail(f): f.seek(0,2)#移動到檔案EOF,參考:[seek](http://docs.python.org/2/library/stdtypes.html?highlight=file#file.seek) while True: line = f.readline() #讀取檔案中新的文字行 if not line: time.sleep(0.1) continue yield line def grep(lines,searchtext): for line in lines: if searchtext in line: yield line
呼叫:
flog = tail(open('warn.log')) pylines = grep(flog,'python') for line in pylines: print line,
例項二:
用yield實現斐波那契數列:
def fibonacci(): a=b=1 yield a yield b while True: a,b = b,a+b yield b
呼叫:
for num in fibonacci(): if num > 100: break print num,
yield中return的作用
作為生成器,因為每次迭代就會返回一個值,所以不能顯示的在生成器函式中return 某個值,包括None值也不行,否則會丟擲“SyntaxError”的異常,但是在函式中可以出現單獨的return,那麼久丟擲StopIteration表示終止迭代。 通過固定長度的緩衝區不斷讀檔案,防止一次性讀取出現記憶體溢位的例子:
def read_file(path): size = 1024 with open(path,'r') as f: while True: block = f.read(SIZE) if block: yield block else: return
如果是在函式中return 具體某個值,就直接拋異常了
>>> def test_return(): ... yield 4 ... return 0 ... File "<stdin>", line 3 SyntaxError: 'return' with argument inside generator
補充:生成器與迭代器的異同
迭代器的類是有一個next()方法和__iter__()的物件,iter()返回物件本身self,所有生成器都是迭代器,但是反過來迭代器不一定是生成器。生成器就如文章開頭的定義。前段時間CPyUG郵件列表對這個問題討論比較激烈,有興趣的可以去看看。與yield有關的一個很重要的概念叫協程,下篇文章將系統的學習下。
本篇文章是改編於自己的原來的部落格http://liuzhijun.iteye.com/blog/1852369,現做了部分修改。
關注公眾號「Python之禪」(id:vttalk)獲取最新文章