1. 程式人生 > >python 叠代器和生成器

python 叠代器和生成器

讀取 一個 很大的 需要 元素 for 循環 pri readlines 操作

叠代器

Iterator是叠代器的意思,它的作用是一次產生一個數據項,直到沒有為止。這樣在 for 循環中就可以對它進行循環處理了。那麽它與一般的序列類型(list, tuple等)有什麽區別呢?它一次只返回一個數據項,占用更少的內存。但它需要記住當前的狀態,以便返回下一數據項。它是一個有著next()方法的對象。而序列類型則保存了所有的數據項,它們的訪問是通過索引進行的。

叠代器是一個實現了叠代器協議的對象,Python中的叠代器協議就是有next方法的對象會前進到下一結果,而在一系列結果的末尾是,則會引發StopIteration。

技術分享

在for循環中,Python將自動調用工廠函數iter()獲得叠代器,自動調用next()獲取元素,還完成了檢查StopIteration異常的工作。

技術分享

常用的幾個內建數據結構tuple、list、set、dict都支持叠代器,字符串也可以使用叠代操作。

你也可以自己實現一個叠代器,如上所述,只需要在類的__iter__方法中返回一個對象,這個對象擁有一個next()方法,這個方法能在恰當的時候拋出StopIteration異常即可。但是需要自己實現叠代器的時候不多,即使需要,使用生成器會更輕松。

技術分享
#!/usr/bin/env python
# coding=utf-8

class test:
    def __init__(self, input_list):
        self.list = input_list
        self.i = 0

    def __iter__(self):
        return self
    
    def next(self):
        if self.i == len(self.list):
            self.i = 0
            raise StopIteration
        self.i += 1
        return  self.list[self.i - 1]
技術分享

技術分享

使用叠代器一個顯而易見的好處就是:每次只從對象中讀取一條數據,不會造成內存的過大開銷。

例如:

技術分享
/* 把文件一次加載到內存中,然後逐行打印。當文件很大時,這個方法的內存開銷就很大了 */
for line in open("test.txt").readlines():
    print line

/* 這是最簡單也是運行速度最快的寫法,他並沒顯式的讀取文件,而是利用叠代器每次讀取下一行 */
for line in open("test.txt"):   #use file iterators
    print line
技術分享

生成器

生成器是創建叠代器的一種簡便的方法。生成器是一個特殊的函數。我們可以從靜態和動態兩個角度理解生成器函數。

首先,從靜態的角度,生成器函數在代碼中表現為:

  1. 含有yield語句(無論yield是否可能會被執行)
  2. 無return或者僅有無值return(一旦函數裏存在yield語句,有值return會視為語法錯誤)

其次,從動態的角度,生成器函數在運行過程中:

  1. 當生成器函數被調用的時候,生成器函數不執行內部的任何代碼,直接立即返回一個叠代器。
  2. 當所返回的叠代器第一次調用next的時候,生成器函數從頭開始執行,如果遇到了執行yield x,next立即返回yield值x。
  3. 當所返回的叠代器繼續調用next的時候,生成器函數從上次yield語句的下一句開始執行,直到遇到下一次執行yield
  4. 任何時候遇到函數結尾,或者return語句,拋出StopIteration異常

特別的,生成器返回的叠代器,其__iter__返回其自身。

生成器的編寫方法和函數定義類似,只是在return的地方改為yield。

生成器中可以有多個yield。當生成器遇到一個yield時,會暫停運行生成器,返回yield後面的值。當再次調用生成器的時候,會從剛才暫停的地方繼續運行,直到下一個yield。

生成器自身又構成一個叠代器,每次叠代時使用一個yield返回的值。

技術分享

需要註意的是,生成器中不需要return語句,不需要指定返回值,在生成器中已經存在默認的返回語句

生成器表達式

(i for i in range(5))
// 返回叠代器
<generator object <genexpr> at 0x7ff3e8f0d960>

列表解析,返回list

[i for i in range(5)]
// 返回list
[0, 1, 2, 3, 4]

在這裏存在一個問題,那就是range(5)會返回一個長度為5的數據,如果是range(1000)那麽就會占用一個1000大小的數組空間;如果我們采用`生成器`,在需要的時候產生一個數字,那麽空間的占用情況就會降低,這裏我們可以使用xrange()函數來實現。

技術分享
‘‘‘
xrange
    函數說明:用法與range完全相同,所不同的是生成的不是一個數組,而是一個生成器。
xrange示例:
‘‘‘ 
>>> xrange(5)
xrange(5)
>>> list(xrange(5))
[0, 1, 2, 3, 4]
>>> xrange(1,5)
xrange(1, 5)
>>> list(xrange(1,5))
[1, 2, 3, 4]
>>> xrange(0,6,2)
xrange(0, 6, 2)
>>> list(xrange(0,6,2))
[0, 2, 4]
技術分享

所以xrange做循環的性能比range好,尤其是返回很大的時候,盡量用xrange吧,除非你是要返回一個列表。

所以,Python 2.X 的 range和xrange有何區別?答案是,range的返回值就是一個list,在你調用range的時候,Python會產生所有的元素。而xrange是一個特別設計的可叠代對象,它在建立的時候僅僅保存終止值。你可比較以下兩種寫法的實際運行結果:

for v in range(1000000000000): #possible Memory Error
    if v == 2:
        break
    
for v in xrange(1000000000000): #fine
    if v == 2:
        break

  

在Python 3.X 中,不再有內建的xrange,其range等效於Python 2.X 的xrange

python 叠代器和生成器