python迭代器和生成器

python 迭代器和生成器

1、迭代器

一、什麼是迭代器

說迭代器以前有個相關的名詞須要介紹:
可迭代對象:只要定義了__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

2、生成器

一、什麼是生成器

生成器函數在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,若是在執行過程當中 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,咱們再也不須要編寫讀文件的迭代類,就能夠輕鬆實現文件讀取。

相關文章
相關標籤/搜索