[Python學習]Iterator 和 Generator的學習心得


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

使用Iterator的好處除了節省內存外,還有一個好處就是可以把非線性化的處理轉換成線性化的方式來進行處理。如對一棵樹的訪問,傳統的方法可以使用遞歸函數來處理,下面是對樹的一個中序遍歷的示例:

例1:

def deal_tree(node):
    if not node:
        return
    if node.leftnode:
        deal_tree(node.leftnode)
    process(node)
    if node.rightnode:
        deal_tree(node.rightnode)

deal_tree(root)

可以看出,對結點的處理函數與遞歸函數是混在一起的,不是很清晰。使用Iterator的方式改寫后為:

例2:

1    def walk_tree(node):
2        if not node:
3            return
4        if node.leftnode:
5            for i in walk_tree(node.leftnode):
6                yield i
7        yield node
8        if node.rightnode:
9            for i in walk_tree(node.rightnode):
10               yield i
11
12   for node in wald_tree(root):
13       process(node)

生成結點的過程仍然是一個遞歸過程,但對於返回后的結點的處理就變成了線性化的處理,結構上要清晰多了。第5-6,9-10行要特別注意,如果不這樣處理直接調用walk_tree的話,其實返回的是一個Iterator對象,而不是想要的元素。

象上面的walk_tree函數在 Python 中可以叫作Generator–產生器,它的作用是生成一個Iterator的對象。那么它主要是將一個函數過程進行封裝,轉化為Iterator對象,每執行到yield語句時,函數的狀態,數據都保存起來,然后返回相應的值。取下一個值的時候,再從上次運行的地方繼續運行,如果遇上yield語句,則再次保存狀態,返回結果,如果不存在值了,則自動引發一個異常StopIteration,從而Iterator不再產生新的值。從此處我們可以了解,這里的Iterator只可以遍歷一次,但並非所有的都是這樣,你完全可以對其進行控制。

下面我再介紹一下如何構造自已的Iterator。很簡單,創建一個類,滿足Iterator的協議,也就是要定義__iter__方法,它返回一個Iterator對象,這個對象必須有next方法,因此我們可以總結出兩種對象模式:

class A:
    def __iter__(self):
        return self

    def next(self):
        if has_next_value(self):
            return next_value
        else:
            raise StopIteration

class B:
    def __iter__(self):
        return iterator_obj

A,B分別為兩種對象模式(都是示例代碼)。模式A表示,在A中定義了next方法,因此__iter__簡單地返回自身即可。當不存在下一個值時,引發StopIteration異常。模式B表示,它使用了其它的Iterator對象,因此只需要定義__iter__即可,next不需要定義,因為返回的Iterator對象已經含有next方法了。如果是自已實現next方法,那么在返回值之前需要記住當前的狀態,以便下一次運行時,可以取下一個值。

第2個例子好象與這里講的不一樣啊。這就是前面講的Generator,它的作用就是把一個函數轉換成一個Iterator,它自動保存狀態,中間數據,引發異常,全部是自動化了。而且它只可以遍歷一次。如果想再次遍歷,只有重新生成新的Iterator對象才可以。

在最新的 Python 2.4 版中新增了Genetaor Expression方式,它是用來生成簡單的,在函數調用需要序列參數時的一種Iterator寫法,語法就象是list comprehension的格式,如:

>>> sum(i*i for i in range(10))                 # sum of squares
285

不過這種寫法必須要在小括號對中,因此它的使用是有限的。它的目的主要是想更好的使用內存。

前面我們提到不是所有的Iterator只可以遍歷一次(使用Generator生成的只能遍歷一次),你完全可以控制它重新遍歷。比如我們可以在Iterator對象中增加一個復位方法,用來將內部的計數恢復到開始狀態,這樣我們就可以重新遍歷了。

下面我們總結一下:

Iterator對象:具有__iter__方法,和next方法。當沒有新值時引發StopIteration異常。

Iterator的好處:在某些情況下可以使程序結構清晰,如將遞歸等非線性處理轉為線性處理。可以減少內存的占用。

Generator:將一個函數轉化成Iterator對象的方法。使用它只需要在函數中需要返回值的時候調用yield語句。它是生成Iterator對象的簡單方法,只適用於函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM