目錄python
說迭代器以前有個相關的名詞須要介紹:
可迭代對象:只要定義了__iter__()方法,咱們就說該對象是可迭代對象,而且可迭代對象能提供迭代器。併發
在Python中,for循環能夠用於Python中的任何類型,包括列表、元祖等等,實際上,for循環可用於任何「可迭代對象」,這其實就是迭代器。app
迭代器是一個實現了迭代器協議的對象,Python中的迭代器協議就是有__next__方法的對象會前進到下一結果,而到一系列結果的末尾,則會引起StopIteration。任何這類的對象在Python中均可以用for循環或其餘遍歷工具迭代,迭代工具內部會在每次迭代時調用next方法,而且捕捉StopIteration異常來肯定什麼時候離開。函數
使用迭代器一個顯而易見的好處就是:每次只從對象中讀取一條數據,不會形成內存的過大開銷。工具
好比要逐行讀取一個文件的內容,利用readlines()方法,咱們能夠這麼寫:線程
for line in open("test.txt").readlines(): print line
這樣雖然能夠工做,但不是最好的方法。由於他其實是把文件一次加載到內存中,而後逐行打印。當文件很大時,這個方法的內存開銷就很大了。code
利用file的迭代器,咱們能夠這樣寫:對象
for line in open("test.txt"): #use file iterators print line
這是最簡單也是運行速度最快的寫法,他並沒顯式的讀取文件,而是利用迭代器每次讀取下一行。接口
使用內建的工廠函數iter(iterable)能夠獲取迭代器對象(對象包含__iter__方法便可迭代,__iter__方法返回一個迭代器):內存
>>> lst = range(5) >>> it = iter(lst) >>> it <listiterator object at 0x0000000001E43390>
使用next()方法訪問下一個元素
>>> it.next() 0 >>> it.next() 1 >>> it.next() 2
python處理迭代器越界是拋出StopIteration異常
>>> it.next() 3 >>> it.next <method-wrapper 'next' of listiterator object at 0x01A63110> >>> it.next() 4 >>> it.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
瞭解了StopIteration,可使用迭代器進行遍歷了:
lst = range(5) it = iter(lst) try: while True: val = it.next() print val except StopIteration: pass
for語法糖:
>>> lst = range(5) >>> for i in lst: ... print i ... 0 1 2 3 4
生成器函數在Python中與迭代器協議的概念聯繫在一塊兒。簡而言之,包含yield語句的函數會被特意編譯成生成器。當函數被調用時,他們返回一個生成器對象,這個對象支持迭代器接口。函數也許會有個return語句,但它的做用是用來yield產生值的。
Python使用生成器對延遲操做提供了支持。所謂的延遲操做,是指在須要的時候才產生結果,而不是當即產生結果。因此生成器也有了以下的好處:
使用斐波那契數列的例子來講明一下:
def fab(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n+ 1 #執行 for n in fab(5): print n 1 1 2 3 5
該函數經過yield關鍵字來返回結果,每一次迭代就中止於yield語句處,一直到下一次迭代。
生成器也是一種迭代器,簡單地講,yield 的做用就是把一個函數變成一個 generator,帶有 yield 的函數再也不是一個普通函數,Python 解釋器會將其視爲一個 generator,調用 fab(5) 不會執行 fab 函數,而是返回一個 iterable 對象!在 for 循環執行時,每次循環都會執行 fab 函數內部的代碼,執行到 yield b 時,fab 函數就返回一個迭代值,下次迭代時,代碼從 yield b 的下一條語句繼續執行,而函數的本地變量看起來和上次中斷執行前是徹底同樣的,因而函數繼續執行,直到再次遇到 yield。看起來就好像一個函數在正常執行的過程當中被 yield 中斷了數次,每次中斷都會經過 yield 返回當前的迭代值。
也能夠手動調用 fab(5) 的 next() 方法(由於 fab(5) 是一個 generator 對象,該對象具備 next() 方法),這樣咱們就能夠更清楚地看到 fab 的執行流程:
>>> f = fab(3) >>> f.next() 1 >>> f.next() 1 >>> f.next() 2 >>> f.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
在一個生成器中,若是沒有return,則默認執行到函數完畢;若是遇到return,若是在執行過程當中 return,則直接拋出 StopIteration 終止迭代。例如:
def read_file(fpath): BLOCK_SIZE = 1024 with open(fpath, 'rb') as f: while True: block = f.read(BLOCK_SIZE) if block: yield block else: return
若是直接對文件對象調用 read() 方法,會致使不可預測的內存佔用。好的方法是利用固定長度的緩衝區來不斷讀取文件內容。經過 yield,咱們再也不須要編寫讀文件的迭代類,就能夠輕鬆實現文件讀取。