理解Python的迭代器

Jesson發表於2015-08-16

首先,廖雪峰老師的教程中解釋了迭代器和生成器,這篇文章只是補充和我個人的總結。

什麼是迭代

可以直接作用於for迴圈的物件統稱為可迭代物件(Iterable)。

可以被next()函式呼叫並不斷返回下一個值的物件稱為迭代器(Iterator)。

所有的Iterable均可以通過內建函式iter()來轉變為Iterator。

對迭代器來講,有一個__next()就夠了。在你使用for 和 in 語句時,程式就會自動呼叫即將被處理的物件的迭代器物件,然後使用它的next__()方法,直到監測到一個StopIteration異常。

上面例子中,列表L可以被for進行迴圈但是不能被內建函式next()用來查詢下一個值,所以L是Iterable。

L通過iter進行包裝後設為I,I可以被next()用來查詢下一個值,所以I是Iterator。

題外話:

  1. 內建函式iter()僅僅是呼叫了物件的__iter()方法,所以list物件內部一定存在方法iter__()
  2. 內建函式next()僅僅是呼叫了物件的__next()方法,所以list物件內部一定不存在方法next__(),但是Itrator中一定存在這個方法。
  3. for迴圈內部事實上就是先呼叫iter()把Iterable變成Iterator在進行迴圈迭代的。

4.Iterator繼承自Iterable,從下面的測試中可以很方便的看到Iterator包含__iter()和next()方法,而Iteratble僅僅包含iter__()。

 

iterable需要包含有__iter()方法用來返回iterator,而iterator需要包含有next__()方法用來被迴圈

如果我們自己定義迭代器,只要在類裡面定義一個 iter() 函式,用它來返回一個帶 next() 方法的物件就夠了。

直接上程式碼

上面的程式碼實現的是找到10以內的奇數,程式碼中的類名可以隨便取,不是一定需要使用我上面提供的類名的。

如果在Iterator的__next__方法中沒有實現StopIteration異常,那麼則是表示的全部奇數,那麼需要在呼叫的時候設定退出迴圈的條件。

我們通過range來實現列印多少個元素,這裡表示列印5個元素,返回結果和上面一致。

當然,我們可以把這兩個類合併在一起,這樣實現程式的簡練。
最終版本如下

 

複製迭代器

迭代器是一次性消耗品,使用完了以後就空了,請看。

當迴圈以後就殆盡了,再次使用呼叫時會引發StopIteration異常。

我們想通過直接賦值的形式把迭代器儲存起來,可以下次使用。
但是通過下面的範例可以看出來,根本不管用。

那怎麼樣才能達到我們要的效果呢?

我們需要使用copy包中的deepcopy了,請看下面:

補充:迭代器不能向後移動, 不能回到開始。

所以需要做一些特殊的事情才能實現向後移動等功能。

以上程式碼均在Python 3.4 中測試通過。

日誌:

  1. 8月13日完成
  2. 8月14日新增關於Iterator, Iterable的更多解釋在題外話的第4點。

相關文章