本篇文章簡單談談可迭代對象,迭代器和產生器之間的關係。
三者簡要關係圖
可迭代對象與迭代器
剛開始我認為這兩者是等同的,但後來發現並不是這樣;下面直接拋出結論: 1)可迭代對象包含迭代器。
2)如果一個對象擁有__iter__方法,其是可迭代對象;如果一個對象擁有next方法,其是迭代器。
3)定義可迭代對象,必須實現__iter__方法;定義迭代器,必須實現__iter__和next方法。
你也許會問,結論3與結論2是不是有一點矛盾。既然一個對象擁有了next方法就是迭代器,那為什麼迭代器必須同時實現兩方法呢。 因為結論1,迭代器也是可迭代對象,因此迭代器必須也實現__iter__方法。
介紹一下上面涉及到的兩個方法:
1)__iter__()
該方法返回的是當前對象的迭代器類的執行個體。因為可迭代對象與迭代器都要實現這個方法,因此有以下兩種寫法。
寫法一:用於可迭代對象類的寫法,返回該可迭代對象的迭代器類的執行個體。
寫法二:用於迭代器類的寫法,直接返回self(即自己本身),表示自身即是自己的迭代器。
也許有點暈,沒關係,下面會給出兩寫法的例子,我們結合具體例子看。
2)next()
返回迭代的每一步,實現該方法時注意要最後超出邊界要拋出StopIteration異常。
下面舉個可迭代對象與迭代器的例子:
#!/usr/bin/env python# coding=utf-8class MyList(object): # 定義可迭代對象類 def __init__(self, num): self.data = num # 上邊界 def __iter__(self): return MyListIterator(self.data) # 返回該可迭代對象的迭代器類的執行個體class MyListIterator(object): # 定義迭代器類,其是MyList可迭代對象的迭代器類 def __init__(self, data): self.data = data # 上邊界 self.now = 0 # 當前迭代值,初始為0 def __iter__(self): return self # 返回該對象的迭代器類的執行個體;因為自己就是迭代器,所以返回self def next(self): # 迭代器類必須實現的方法 while self.now < self.data: self.now += 1 return self.now - 1 # 返回當前迭代值 raise StopIteration # 超出上邊界,拋出異常my_list = MyList(5) # 得到一個可迭代對象print type(my_list) # 返回該對象的類型my_list_iter = iter(my_list) # 得到該對象的迭代器執行個體,iter函數在下面會詳細解釋print type(my_list_iter)for i in my_list: # 迭代 print i
運行結果:
問題:上面的例子中出現了iter函數,這是什麼東西。和__iter__方法有關係嗎。
其實該函數與迭代是息息相關的,通過在Python命令列中列印“help(iter)”得知其有以下兩種用法。
用法一:iter(callable, sentinel)
不停的調用callable,直至其的傳回值等於sentinel。其中的callable可以是函數,方法或實現了__call__方法的執行個體。
用法二:iter(collection)
1)用於返回collection對象的迭代器執行個體,這裡的collection我認為表示的是可迭代對象,即該對象必須實現__iter__方法; 事實上iter函數與__iter__方法聯絡非常緊密,iter()是直接調用該對象的__iter__(),並把__iter__()的返回結果作為自己的傳回值,故該用法常被稱為“建立迭代器”。
2)iter函數可以顯示調用,或當執行“for i in obj:”,Python解譯器會在第一次迭代時自動調用iter(obj),之後的迭代會調用迭代器的next方法,for語句會自動處理最後拋出的StopIteration異常。
通過上面的例子,相信對可迭代對象與迭代器有了更具體的認識,那麼產生器與它們有什麼關係呢。下面簡單談一談
產生器
產生器是一種特殊的迭代器,產生器自動實現了“迭代器協議”(即__iter__和next方法),不需要再手動實現兩方法。
產生器在迭代的過程中可以改變當前迭代值,而修改普通迭代器的當前迭代值往往會發生異常,影響程式的執行。
看一個產生器的例子:
#!/usr/bin/env python# coding=utf-8def myList(num): # 定義產生器 now = 0 # 當前迭代值,初始為0 while now < num: val = (yield now) # 返回當前迭代值,並接受可能的send發送值;yield在下面會解釋 now = now + 1 if val is None else val # val為None,迭代值自增1,否則重新設定當前迭代值為valmy_list = myList(5) # 得到一個產生器對象print my_list.next() # 返回當前迭代值print my_list.next()my_list.send(3) # 重新設定當前的迭代值print my_list.next()print dir(my_list) # 返回該對象所擁有的方法名,可以看到__iter__與next在其中
運行結果:
具有yield關鍵字的函數都是產生器,yield可以理解為return,返回後面的值給調用者。不同的是return返回後,函數會釋放,而產生器則不會。在直接調用next方法或用for語句進行下一次迭代時,產生器會從yield下一句開始執行,直至遇到下一個yield。
參考資料:
Python核心編程第二版11.10節,13.13.3節
完全理解Python迭代對象、迭代器、產生器
深入講解Python中的迭代器和產生器
如何更好地理解Python迭代器和產生器
文中如有不恰當的地方,還望包容和指出,感謝