python迭代器資料整理

發表於2016-07-11

什麼是迭代器

相關概念定義

迭代器(Iterator):

滿足迭代協議的物件就是迭代器
iterator就是實現了Iteration Protocol的物件,這類物件都支援迴圈遍歷的操作(for/while/支援迭代的函式list() sum()…)。

迭代協議(Iteration Protocol):

內建函式iter()接收一個可迭代物件,並返回一個可迭代物件.
每次將這個可迭代物件傳遞給next()函式,都會返回它所包含的下一個元素,當迭代完最後一個元素時,就會觸發StopIteration異常。

滿足以上要求的物件,就是迭代器。

迭代隱含的操作

在每次的迭代語句中,python都會按照迭代協議去對迭代器進行迭代。其實,在實際執行中,python會進行一些其他的操作:

  1. 將需要迭代的物件作為引數傳遞給iter函式
  2. iter返回一個迭代器物件
  3. 每次迴圈則將返回的迭代器物件傳遞給next函式
  4. 迴圈至最後一個元素,觸發StopIteration

for語句為例
當我們在Python中執行迴圈語句for i in foo的時候,其背後的操作是:

  1. foo = iter(foo)
  2. next(foo)

next(foo)在python3中執行的是:foo.__next__(),在python2中則是:foo.next()

迭代器的實現

迭代器是用class來實現的。其中必需實現的有兩個方法:__iter__next(python2)/__next__(python3)。其中,__iter__必需返回一個迭代器物件,next則負責迭代邏輯並在迭代完畢時觸發異常。

如下:


迭代器的特殊情況

迭代器的__iter__返回self

迭代器的__iter__方法需要返回的是一個具有next方法的可迭代物件。如果當__iter__返回的是self的話,就會產生其他意想不到的效果。

執行結果:

yrange中,iter返回的是self,在執行list(y)iter返回的都是同一個self,所以再次呼叫list(y)時只會觸發結束迭代異常,列表中並無內容。
而在zrange中,每次執行list(z)時,iter都是返回一個新的迭代器zrange(self.n),所以每次執行list(z)都得到完整的元素。

生成器的迭代

通常,對於資料量特別大的序列,我們會用生成器generator來代替容器物件container,這樣可以利用lazy evaluable來節省記憶體開銷。值得注意的是,生成器也是一個只能迭代一次的迭代器。

執行結果:

如果是利用便捷的生成器表示式也是一樣:

要解決這個問題,可以將迭代器和生成器組合使用:

結果:

值得注意的是,平常我們利用到生成器的地方都是資料量特別大的情況,這個時候,其實應該儘量避免多次迭代生成器。我想這應該也是python沒有支援對生成器多次迭代的特性的原因。

程式設計建議

在實際的程式設計中,往往需要在函式中多次迭代一個序列,如果這個序列是呼叫API得到的,而你又不能保證它是沒有陷阱的迭代器時。可以在遍歷迭代器的時候,加入一個判斷語句,避免無法多次迭代的情況發生:

參考資料

相關文章