迭代器 迭代器是在python2.2中被加入的,它為類序列對象提供了一個類序列的接口。有了迭代器可以迭代一個不是序列的對象,因為他表現出了序列的行為。當在python中使用for循環迭代一個對象時,調用者幾乎分辨不出他迭代的是一個迭代器對象還是一個序列對象,因為python讓他(迭代器)像一個序列那樣操作。 如何迭代 本質上說迭代器是個對象,但是這個對象有個特殊的方法next()(在python3中使用__next__()代替了next方法)。當使用for循環來遍歷整個對象時候,就會自動調用此對象的__next__()方法並獲取下一個item。當所有的item全部取出后就會拋出一個StopIteration異常,這並不是錯誤的發生,而是告訴外部調用者迭代完成了,外部的調用者嘗試去捕獲這個異常去做進一步的處理。 不過迭代器是有限制的,例如 不能向后移動 不能回到開始 也無法復制一個迭代器。 因此要再次進行迭代只能重新生成一個新的迭代器對象。 獲取迭代器 對於python內置的可迭代(iterable)對象,可以通過內置的iter()函數來獲取相應的迭代器對象。 Python a = [1,2,3,45] type(a) list a = iter(a) type(a) list_iterator 1 2 3 4 5 6 7 8 9
a = [1,2,3,45] type(a) list
a = iter(a) type(a) list_iterator 這樣就獲取了list相應的迭代器對象。 我們來看一下該迭代器對象的屬性: Python dir(a) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__'] 可見此迭代對象具有兩個特殊的成員方法__iter__()和__next__(),這兩個方法便是支持迭代器協議所需要實現的方法。其中__iter__()方法返回迭代器對象本身,__next__()方法返回容器的下一個元素,直到結尾拋出StopIteration異常。 我們來測試一下這個list_iterator對象的這兩個方法: __iter__()返回的對象就是迭代器對象本身。 Python a = [1,2,3,45] a = iter(a) a.__iter__() <list_iterator at 0x3a33f10>a <list_iterator at 0x3a33f10>a is a.__iter__() True In [1]: a = [1,2,3,45] In [2]: a = iter(a) In [3]: a.__iter__() Out[3]: <list_iterator at 0x3a33f10> In [4]: a Out[4]: <list_iterator at 0x3a33f10> In [5]: a is a.__iter__() Out[5]: True In [6]: __next__()方法返回容器中的值直到結尾。 Python In [6]: a.__next__() Out[6]: 1 In [7]: a.__next__() Out[7]: 2 In [8]: a.__next__() Out[8]: 3 In [9]: a.__next__() Out[9]: 45 In [10]: a.__next__() ------------------------------------------ StopIteration Traceback (most recent call last) <ipython-input-10-73aa2c76d676> in <module>() ----> 1 a.__next__() In [6]: a.__next__() Out[6]: 1 In [7]: a.__next__() Out[7]: 2 In [8]: a.__next__() Out[8]: 3 In [9]: a.__next__() Out[9]: 45 In [10]: a.__next__() ------------------------------------------ StopIteration Traceback (most recent call last) <ipython-input-10-73aa2c76d676> in <module>() ----> 1 a.__next__() StopIteration: In [11]: 2. 創建迭代器對象 除了使用iter()函數將內置的序列對象轉換成相應的迭代器,我們可以自己實現迭代器協議創建迭代器對象,要實現迭代器協議也就是要在類中實現__iter__()和__next__()方法。 下面我寫一個與list_iterator相同行為的迭代器: Python class ListIter(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): return self def __next__(self): if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration class ListIter(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): return self def __next__(self): if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration 我們就可以使用for循環來遍歷這個迭代器了: Python In [16]: a = ListIter([1,2,3,4,5]) In [17]: for i in a: ....: print(i) ....: In [16]: a = ListIter([1,2,3,4,5]) In [17]: for i in a: ....: print(i) ....: 對於迭代器對象,使用for循環遍歷整個數組其實是個語法糖,他的內部實現還是通過調用對象的__next__()方法。 實際上他內部的工作原理應該是這樣的: a = ListIter([1, 2, 3, 4, 5]) while True: try: i = a.__next__() except StopIteration: break // do something in for loop print(i) a = ListIter([1, 2, 3, 4, 5]) while True: try: i = a.__next__() except StopIteration: break // do something in for loop print(i) 迭代器支持多次迭代 正如前面所說的迭代器對象不支持重新迭代,也就是同一個迭代器對象無法多次迭代,如: Python In [19]: a = ListIter([1,2,3,4,5]) In [20]: [i for i in a] Out[20]: [1, 2, 3, 4, 5] In [21]: [i for i in a] Out[21]: [] In [19]: a = ListIter([1,2,3,4,5]) In [20]: [i for i in a] Out[20]: [1, 2, 3, 4, 5] In [21]: [i for i in a] Out[21]: [] In [22]: 可見,當我再次迭代迭代器a的時候便只返回了空列表,這是因為for循環直接捕獲了StopIteration異常。如果要再次迭代生成列表的話只能重新生成一個新的迭代器對象。 為了能夠解決這個問題,可以分別定義一個可迭代對象(iterables)和迭代器對象(iterator). 插入小插曲: 對於可迭代對象和迭代器對象,我的理解是: 可迭代對象是實現了__iter__()方法的對象,__iter__()可以返回一個迭代器對象。 迭代器對象是實現了__next__()方法的對象,其中他的__iter__()返回的是迭代器對象本身。 我把代碼做了修改,如下: Python class ListIterable(object): def __init__(self, data): self.__data = data def __iter__(self): print("call iterable __iter__().") return ListIterator(self.__data) class ListIterator(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): print("call iterator __iter__().") return self def __next__(self): print("call iterator __next__().") if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration class ListIterable(object): def __init__(self, data): self.__data = data def __iter__(self): print("call iterable __iter__().") return ListIterator(self.__data) class ListIterator(object): def __init__(self, data): self.__data = data self.__count = 0 def __iter__(self): print("call iterator __iter__().") return self def __next__(self): print("call iterator __next__().") if self.__count < len(self.__data): val = self.__data[self.__count] self.__count += 1 return val else: raise StopIteration 為了知道python何時調用__iter__()方法,我添加了一個printf函數來做標記。 現在把這兩個類導入到當前空間中: Python In [1]: from list_iter import * In [2]: a = ListIterable([1,2,4,5,6]) In [3]: b = a.__iter__() call iterables __iter__(). In [4]: a Out[4]: <list_iter.ListIterable at 0x39446d0> In [5]: b Out[5]: <list_iter.ListIterator at 0x39447b0> In [1]: from list_iter import * In [2]: a = ListIterable([1,2,4,5,6]) In [3]: b = a.__iter__() call iterables __iter__(). In [4]: a Out[4]: <list_iter.ListIterable at 0x39446d0> In [5]: b Out[5]: <list_iter.ListIterator at 0x39447b0> In [6]: 可見a是iterable對象(實現了__iter__()),b是iterator對象(實現了__next__())。 下面看看這樣做是不是就可以重復多次迭代了: Python In [6]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[6]: [1, 2, 4, 5, 6] In [7]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[7]: [1, 2, 4, 5, 6] In [6]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[6]: [1, 2, 4, 5, 6] In [7]: [i for i in a] call iterable __iter__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). call iterator __next__(). Out[7]: [1, 2, 4, 5, 6] In [8]: 重復迭代是可以了,從輸出中我們可以看出一些什么來 我們在使用迭代工具對iterable對象進行迭代的時候首先調用的是iterable的__iter__()方法,返回一個迭代器對象,也就是ListIterator的實例。 然后再遍歷的時候是調用iterator的next方法輸出值。 這樣就可以解釋了為什么這樣處理能夠多次迭代了,因為每次使用迭代工具迭代的時候都會調用__iter__()返回一個新的迭代器對象,這樣就相當於創建多個迭代器了,自然可以看起來是重復迭代了! 可變對象和迭代器 在迭代可變對象時候,一個序列的迭代器只是記錄當前到達了序列中的第幾個元素,所以如果在迭代過程中改變了序列的元素。更新會立即反應到所迭代的條目上。 我寫了個測試看了下,的確: Python In [13]: c = [1,2,3,4,5] In [14]: d = iter(c) In [15]: for i in c: ....: print(i) ....: c.remove(i) ....: In [13]: c = [1,2,3,4,5] In [14]: d = iter(c) In [15]: for i in c: ....: print(i) ....: c.remove(i) ....: 可見上面邊迭代邊刪除列表的元素,但是最后卻只輸出了1, 3, 5,這是為啥? 既然迭代器只記得是在列表中第幾個元素,那么當在第0個元素的時候將會輸出1然后刪除1,這是列表變成了 Python [2, 3, 4, 5] 1 [2, 3, 4, 5] 但是迭代器記得我是在第二個位置上面,就指向了列表中的第二個位置上,也就是3,然后輸出3. 以此類推,最后只能輸出1,3,5了。 如果我猜測的沒錯的話,剩余的列表應該只剩下2和4了: Python In [17]: c Out[17]: [2, 4] 1 2 In [17]: c Out[17]: [2, 4]