在Python中,有這兩個概念容易讓人混淆。第一個是可迭代對象(Iterable),第二個是迭代器(Iterator),第三個是生成器(Generator),這裡暫且不談生成器。
可迭代對象
列表、元組、字符串、字典等都是可迭代對象,可以使用for循環遍歷出所有元素的都可以稱為可迭代對象(Iterable)。在Python的內置數據結構中定義了Iterable這個類,在collections.abc模塊中,我們可以用這個來檢測是否為可迭代對象
這些數據結構之所以能稱之為Iterable,是因為其內部實現了__iter__()方法,從而可迭代。當我們使用for循環時,解釋器會調用內置的iter()函數,調用前首先會檢查對象是否實現了__iter__()方法,如果有就調用它獲取一個迭代器(接下來會講)。加入沒有__iter__()方法,但是實現了__getitem__()方法,解釋器會創建一個迭代器並且按順序獲取元素。如果這兩個方法都沒有找到,就會拋出TypeError異常。下面我們自定義對象,分別實現這兩個方法(getitem(), iter())
如上所示,這裡沒有實現__iter__方法,只實現了__getitem__方法,也使得Myobj稱為可迭代對象。
下面我們實現__iter__方法,這裡使用了yield語法用來產出值(這裡需要生成器的知識)
這裡同樣讓對象稱為可迭代對象。
迭代器
迭代器是一個可以記住遍歷的位置的對象。 迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。
如上圖所示,迭代器(Iterator)繼承可迭代(Iterable),迭代器必須實現__iter__方法和__next__方法。其中__next__方法用於產出下一個元素。
由繼承圖可見,迭代器一定是可迭代對象,可迭代對象不一定是迭代器
迭代器有兩個基本的方法:iter() 和 next()。
我們使用iter(iterable)即可把可迭代對象轉換成迭代器 使用next(iterator)來獲取迭代器的下一個值
如上所示,因為對象實現了__next__方法,我們可以通過next(iterator)來獲取迭代器的下一個值,直到沒有值了,拋出StopIteration異常結束。
迭代器的背後
迭代器Iterator是一個抽象基類,它定義在_collections_abc.py中
Iterator源碼如下
可以看到,它實現了__subclasshook__方法,即不用顯式繼承Iterator,只需要實現__iter__和__next__方法即可稱為Iterator的虛擬子類。這裡凸現了Python的鴨子類型,實現特定的「協議」即可擁有某種行為。
另外,它自己也定義了__iter__方法,當我們使用iter(Iterator)時直接返回自己,不做任何處理。
iter()函數的兩個用法
官方文檔中給出了說明:
第一個用法:iter(iterable) -> iterator (把可迭代對象轉換為迭代器)
第二個用法:iter(callable, sentinel) -> iterator (第一個參數:任何可調用對象,可以是函數,第二個是標記值,當可調用對象返回這個值時,迭代器拋出StopIteration異常,而不產出標記值)
上面代碼的流程:test_iter函數從values列表中隨機挑選一個值並返回,調用iter(callable, sentinel)函數,把sentinel標記值設置為2,返回一個callable_iterator實例,遍歷這個特殊的迭代器,如果函數返回標記值2,直接拋出異常退出程序。這就是iter函數的鮮為人知的另一個用法。