本文字數:2465 字
閱讀本文大概需要:7 分鐘
寫在之前
今天來講講「迭代器」的內容,其實已經拖了好多天了,感覺再不寫就要忘記了。「迭代」相信對你來說已經不陌生了,我前面曾經專門用一篇文章來講,如果你已經沒有什麼印象的話,就再點進去看看(零基礎學習 Python 之初識迭代)。
迭代器
首先我們先來看一種檢查是否可迭代的方法:
>>> hasattr(list,'__iter__')True
可以用上面的這種方法檢查已經學習過的其他默認類型的對象,比如字符串,列表,字典等是否是可迭代的。
__iter__() 是一個特殊方法,它是迭代規則的基礎,有了它,就說明對象是可迭代的。跟迭代有關的一個內建函數 iter(),這個函數我們在之前的文章中介紹過,它返回的是一個迭代器對象,比如像下面這樣:
>>> list1 = [1,2,3,4]>>> iter_list = iter(list1)>>> iter_list<list_iterator object at 0x00000000021CE438>
從上述代碼的結果可以看出,iter_list 引用的是迭代器對象。那麼在這裡有一個問題,iter_list 和 list1 有區別嗎?我們來試一下:
>>> hasattr(list1,'__iter__')True>>> hasattr(iter_list,'__iter__')True
從上面看出它們都有 __iter__,說明它們都是可迭代的。
>>> hasattr(list1,"__next__")False>>> hasattr(iter_list,"__next__")True
我們把像 iter_list 所引用的對象那樣,稱之為「迭代器對象」。顯而易見的是,迭代器對象必然是可迭代的,反正則不一定。且 Python 中迭代器對象實現的是 __next__() 方法。
為了體現一下 Python 在這的強大之處,我們先來寫一個迭代器對象:
classMyRange:def__init__(self,n):self.i = 1self.n = ndef__iter__(self):returnselfdef__next__(self):ifself.i <= self.n: i = self.iself.i += 1return ielse: raise StopIteration()if __name__ == "__main__": x = MyRange(5) print([i for i in x])
上述代碼的運行結果如下所示:
[1,2,3,4,5]
上述的代碼仿寫了類似 range() 的類,但是與 range() 又有所不同,除了結果不同以外還包括以下 2 點:
1.__iter__() 是類中的核心,它返回了迭代器的本身,一個實現了 __iter__() 方法的對象,就意味著它是可迭代的。
2.實現了 __next__() 方法,從而使得這個對象是迭代器對象。
接下來我們來看看 range() 本身:
>>> a = range(5)>>> hasattr(a,'__iter__')True>>> hasattr(a,'__next__')False>>> print(a)range(0, 5)
由上面我們就可以看出,其實我們所寫的類和 range() 本身還是有很大區別的。
通過上面的內容和我們之前的文章對迭代的講述,下面我們對迭代器做一個概括:
1.在 Python 中,迭代器是遵循迭代協議的對象。我們可以使用 iter() 從任何序列得到迭代器(exp: list,turple,set and so on)。
2.當自己編寫迭代器的類的時候,其中實現 __iter__() 和 __next__() 方法,如果沒有元素的話,會引發 StopIteration 異常。
3.如果有很多值的話,列表會佔用太多的內存,而迭代器則佔用的更少,它從第一個元素開始訪問,直到所有的元素被訪問完結束,只能向前衝,不能後退。
迭代器不僅僅是實用而已,而且也非常的有趣,讓我們來看下面的操作:
>>> list1 = [x**x for x in range(3)]>>> list1[1, 1, 4]>>> for i in list1:print(i)...114>>> for i in list1:print(i)...114
我們在上面重複兩次調用列表 list1 進行循環,都是能正常進行的,這個列表相當於一個可以長久使用的東西,可以重複使用。
在 Python 中,除了列表解析式以外,還可以做成元組解析式,方法也是非常的簡單:
>>> tuple1 = (x**x for x in range(3))>>> tuple1<generator object <genexpr> at 0x0000000001DF16D8>>>> for i in tuple1:print(i)...114>>> for i in tuple1:print(i)...
對於 tuple1,我們可以看到它是一個 generator 對象,關於這個是啥我們先不管,後面我會單獨來說的。當我們把它用到循環中的時候,它明顯是個一次性用品,再次使用的時候它就什麼也不顯示了。
>>> type(list1)<class 'list'>>>> type(tuple1)<class 'generator'>
由上面可以看出,list1 和 tuple1 是兩種不同的對象,它們之間的區別不僅僅是 tuple1 是一個元組這麼簡單,它還是 generator。其它的我們先不管,你可以嘗試一下在交互模式下輸入 dir(tuple1),查看它是否有 __iter__ 和 __next__,我可以先告訴你,是有的。
既然是有的,那麼 tuple1 引用的就是一個迭代器的對象,它的 __next__() 方法促使它只能向前。
寫在之後
迭代器到這就寫完了,從內容來看迭代器確實有其過人之處,但是它不是萬能的,比如它只能向前,不能回退。還有一個是迭代器並不適合在多線程的環境中對可變集合使用,現在這個東西看起來可能還是有點困難,如果以後有機會寫多線程的話,再做解釋。
如果你覺得本篇文章對你有幫助的話,歡迎點讚轉發,讓更多的人可以看到,歡迎大家聯繫我。
The end。