17、Python與設計模式–迭代器模式

途索發表於2017-02-27

一、迭代器與生成器

今天的主角是迭代器模式。在python中,迭代器並不用舉太多的例子,因為python中的迭代器應用實在太多了(不管是python還是其它很多的程式語言中,實際上迭代器都已經納入到了常用的庫或者包中)。而且在當前,也幾乎沒有人專門去開發一個迭代器,而是直接去使用list、string、set、dict等python可迭代物件,或者直接使用__iter__和next函式來實現迭代器。如下例:

if __name__=="__main__":
    lst=["hello Alice","hello Bob","hello Eve"]
    lst_iter=iter(lst)
    print lst_iter
    print lst_iter.next()
    print lst_iter.next()
    print lst_iter.next()
    print lst_iter.next()

列印如下:

hello Alice
hello Bob
hello Eve
Traceback (most recent call last):
File “D:/WorkSpace/Project/PyDesignMode/example.py”, line 719, in
print lst_iter.next()
StopIteration
在這種迭代器的使用過程中,如果next超過了迭代範圍,會丟擲異常。
在python物件的方法中,也可以輕易使用迭代器模式構造可迭代物件,如下例:

class MyIter(object):
    def __init__(self, n):
        self.index = 0
        self.n = n
    def __iter__(self):
        return self
    def next(self):
        if self.index < self.n:
            value = self.index**2
            self.index += 1
            return value
        else:
            raise StopIteration()

__iter__和next實現了迭代器最基本的方法。如下方式進行呼叫:

if __name__=="__main__":
    x_square=MyIter(10)
    for x in x_square:
        print x

列印如下:
0
1
4
9
16
25
36
49
64
81
注意__iter__方法中的返回值,由於直接返回了self,因而該迭代器是無法重複迭代的,如以下業務場景:

if __name__=="__main__":
    x_square=MyIter(10)
    for x in x_square:
        print x
    for x in x_square:
        print x

只能列印一遍平方值。解決辦法是,在__iter__中不返回例項,而再返回一個物件,寫成:

def __iter__(self):
    return MyIter(self.n)

這樣,在每次迭代時都可以將迭代器“初始化”,就可以多次迭代了。
另外,在python中,使用生成器可以很方便的支援迭代器協議。生成器通過生成器函式產生,生成器函式可以通過常規的def語句來定義,但是不用return返回,而是用yield一次返回一個結果,在每個結果之間掛起和繼續它們的狀態,來自動實現迭代協議。
如下例:

def MyGenerater(n):
    index=0
    while index<n:
        yield index**2
        index+=1

注意,這是個函式。在每次呼叫生成器,得到返回結果後,現場得以保留,下次再呼叫該生 成器時,返回保留的現場從yield後繼續執行程式。

if __name__=="__main__":
    x_square=MyGenerater(10)
    for x in x_square:
        print x

列印結果與上面一致。

二、迭代器模式

迭代器模式的定義如下:它提供一種方法,訪問一個容器物件中各個元素,而又不需要暴露物件的內部細節。
f1.png


相關文章