python之路---叠代器和生成器
叠代器
初識叠代器:
叠代器的優點:節省內存
叠代器有兩種:
一種是調用方法直接返回的
一種是可叠代(的)對象通過執行iter方法得到的(一些講的就是這種)
能被for循環,就是可叠代的
可叠代的(iterable):
- str list tuple dict set range() 都可以被for循環,說明是可叠代的
from collections import Iterable l = [1, 2, 3, 4] t= (1, 2, 3, 4) d = {1: 2, 3: 4} s = {1, 2, 3, 4} print(isinstance(l, Iterable)) # True print(isinstance(t, Iterable)) # True print(isinstance(d, Iterable)) # True print(isinstance(s, Iterable)) # True
不能被for循環,就是不可叠代的
不可叠代的:
- int
for i in 1234 print(i) 結果: Traceback (most recent call last): File"test.py", line 4, in <module> for i in 1234: TypeError: ‘int‘ object is not iterable
那就有了一個問題,for循環怎麽知道list,dict等是可叠代的,int就是不可叠代的呢?
答案就是滿足的for的’要求‘的就是可叠代的,不滿足就是不可叠代的。
這個“要求”叫做可叠代協議,就是看該數據類型內部是否實現了__iter__方法,如果該數據類型內部實現了__iter__方法,那就能for循環,能for循環,那就是可叠代的。
l = [1, 2, 3, 4] t = (1, 2, 3, 4) d= {1: 2, 3: 4} s = {1, 2, 3, 4}
print(dir(l)) #dir(l)是列表中實現的所有方法 print(dir(t)) #dir(t)是元組中實現的所有方法 print(dir(d)) #dir(d)是字典中實現的所有方法 print(dir(s)) #dir(s)是集合中實現的所有方法
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__iadd__‘, ‘__imul__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__reversed__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘append‘, ‘clear‘, ‘copy‘, ‘count‘, ‘extend‘, ‘index‘, ‘insert‘, ‘pop‘, ‘remove‘, ‘reverse‘, ‘sort‘] [‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__getnewargs__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘count‘, ‘index‘] [‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘clear‘, ‘copy‘, ‘fromkeys‘, ‘get‘, ‘items‘, ‘keys‘, ‘pop‘, ‘popitem‘, ‘setdefault‘, ‘update‘, ‘values‘] [‘__and__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__iand__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__ior__‘, ‘__isub__‘, ‘__iter__‘, ‘__ixor__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__or__‘, ‘__rand__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__ror__‘, ‘__rsub__‘, ‘__rxor__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__sub__‘, ‘__subclasshook__‘, ‘__xor__‘, ‘add‘, ‘clear‘, ‘copy‘, ‘difference‘, ‘difference_update‘, ‘discard‘, ‘intersection‘, ‘intersection_update‘, ‘isdisjoint‘, ‘issubset‘, ‘issuperset‘, ‘pop‘, ‘remove‘, ‘symmetric_difference‘, ‘symmetric_difference_update‘, ‘union‘, ‘update‘]打印結果
總結一下我們現在所知道的:可以被for循環的都是可叠代的,要想可叠代,內部必須有一個__iter__方法。
接著分析,__iter__方法做了什麽事情呢?(為什麽內部有__iter__方法就是可叠代的)
print([1,2].__iter__()) 結果 <list_iterator object at 0x1024784a8>
執行了list([1,2])的__iter__方法,我們好像得到了一個list_iterator。
所以 [1,2].__iter__() 是一個列表叠代器(list_iterator)
‘‘‘ dir([1,2].__iter__())是列表叠代器中實現的所有方法,dir([1,2])是列表中實現的所有方法,都是以列表的形式返回給我們的,為了看的更清楚,我們分別把他們轉換成集合, 然後取差集。 ‘‘‘ #print(dir([1,2].__iter__())) #print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2]))) 結果: {‘__length_hint__‘, ‘__next__‘, ‘__setstate__‘}
我們看到在列表叠代器中多了三個方法,那麽這三個方法都分別做了什麽事呢?
iter_l = [1,2,3,4,5,6].__iter__() #獲取叠代器中元素的長度 print(iter_l.__length_hint__()) #根據索引值指定從哪裏開始叠代 print(iter_l.__setstate__(4)) #一個一個的取值 print(iter_l.__next__()) print(iter_l.__next__())
打印結果:
6
None
5
6
這三個方法中,能讓我們一個一個取值的神奇方法是誰?
沒錯!就是__next__
在for循環中,就是在(數據類型調用__iter__方法後生成的叠代器)內部調用了__next__方法才能取到一個一個的值。
l = [1,2,3,4] l_iter = l.__iter__() item = l_iter.__next__() print(item) # 1 item = l_iter.__next__() print(item) # 2 item = l_iter.__next__() print(item) # 3 item = l_iter.__next__() print(item) # 4 item = l_iter.__next__() print(item) # 拋出一個異常StopIteration
這是一段會報錯的代碼,如果我們一直取next取到叠代器裏已經沒有元素了,就會拋出一個異常StopIteration,告訴我們,列表中已經沒有有效的元素了。
這個時候,我們就要使用異常處理機制來把這個異常處理掉。
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
那現在我們就使用while循環實現了原本for循環做的事情,我們是從誰那兒獲取一個一個的值呀?是不是就是l_iter?好了,這個l_iter就是一個叠代器。
叠代器遵循叠代器協議:必須擁有__iter__方法和__next__方法。
from collections import Iterator print(isinstance(range(100000000),Iterator)) #驗證range執行之後得到的結果不是一個叠代器
可叠代的對象調用__iter__方法後就是叠代器,如 [1,2].__iter__就是一個叠代器(擁有__iter__和__next__方法)
為什麽要用for循環?
基於上面講的列表這一大堆遍歷方式,聰明的你立馬看除了端倪,於是你不知死活大聲喊道,你這不逗我玩呢麽,有了下標的訪問方式,我可以這樣遍歷一個列表啊
l=[1,2,3] index=0 while index < len(l): print(l[index]) index+=1 #要毛線for循環,要毛線可叠代,要毛線叠代器
沒錯,序列類型字符串,列表,元組都有下標,你用上述的方式訪問,perfect!但是你可曾想過非序列類型像字典,集合,文件對象的感受,所以嘛,年輕人,for循環就是基於叠代器協議提供了一個統一的可以遍歷所有對象的方法,即在遍歷之前,先調用對象的__iter__方法將其轉換成一個叠代器,然後使用叠代器協議(調用__next__方法一個一個取值)去實現循環訪問,這樣所有的對象就都可以通過for循環來遍歷了,而且你看到的效果也確實如此,這就是無所不能的for循環,覺悟吧,年輕人
生成器
叠代器有的好處是可以節省內存。如果在某些情況下,我們也需要節省內存,就只能自己寫。我們自己寫的這個能實現叠代器功能的叫生成器。
python之路---叠代器和生成器