生成器,叠代器
一 遞歸和叠代
略
二 什麽是叠代器協議
1.叠代器協議是指:對象必須提供一個next方法,執行該方法要麽返回叠代中的下一項,要麽就引起一個StopIteration異常,以終止叠代 (只能往後走不能往前退)
2.可叠代對象:實現了叠代器協議的對象(如何實現:對象內部定義一個__iter__()方法)
3.協議是一種約定,可叠代對象實現了叠代器協議,python的內部工具(如for循環,sum,min,max函數等)使用叠代器協議訪問對象。
三 python中強大的for循環機制
for循環的本質:循環所有對象,全都是使用叠代器協議。
正本清源:
很多人會想,for循環的本質就是遵循叠代器協議去訪問對象,那麽for循環的對象肯定都是叠代器了啊,沒錯,那既然這樣,for循環可以遍歷(字符串,列表,元組,字典,集合,文件對象),那這些類型的數據肯定都是可叠代對象啊?但是,我他媽的為什麽定義一個列表l=[1,2,3,4]沒有l.next()方法,打臉麽。
(字符串,列表,元組,字典,集合,文件對象)這些都不是可叠代對象,只不過在for循環式,調用了他們內部的__iter__方法,把他們變成了可叠代對象
然後for循環調用可叠代對象的__next__方法去取值,而且for循環會捕捉StopIteration異常,以終止叠代
1 l=[‘a‘,‘b‘,‘c‘] 2 #一:下標訪問方式 3 print(l[0]) 4 print(l[1]) 5 print(l[2]) 6 # print(l[3])#超出邊界報錯:IndexError 7 8 #二:遵循叠代器協議訪問方式 9 diedai_l=l.__iter__() 10 print(diedai_l.__next__()) 11 print(diedai_l.__next__()) 12 print(diedai_l.__next__()) 13 # print(diedai_l.__next__())#超出邊界報錯:StopIteration14 15 #三:for循環訪問方式16 #for循環l本質就是遵循叠代器協議的訪問方式,先調用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),然後依次執行diedai_l.next(),直到for循環捕捉到StopIteration終止循環
#for循環所有對象的本質都是一樣的原理
17 18 for i in l:#diedai_l=l.__iter__()19 print(i) #i=diedai_l.next()20 21 #四:用while去模擬for循環做的事情22 diedai_l=l.__iter__() 23 while True: 24 try: 25 print(diedai_l.__next__()) 26 except StopIteration: 27 print(‘叠代完畢了,循環終止了‘) 28 break
基於上面講的列表的三種訪問方式,聰明的你立馬看除了端倪,於是你不知死活大聲喊道,你這不逗我玩呢麽,有了下標的訪問方式,我可以這樣遍歷一個列表啊
l=[1,2,3] index=0 while index < len(l): print(l[index]) index+=1 #要毛線for循環,要毛線for循環,要毛線for循環
沒錯,序列類型字符串,列表,元組都有下標,你用上述的方式訪問,perfect!但是你可曾想過非序列類型像字典,集合,文件對象的感受,所以嘛,年輕人,for循環就是基於叠代器協議提供了一個統一的可以遍歷所有對象的方法,即在遍歷之前,先調用對象的__iter__方法將其轉換成一個叠代器,然後使用叠代器協議去實現循環訪問,這樣所有的對象就都可以通過for循環來遍歷了,而且你看到的效果也確實如此,這就是無所不能的for循環,覺悟吧,年輕人
五 生成器初探
什麽是生成器?
可以理解為一種數據類型,這種數據類型自動實現了叠代器協議(其他的數據類型需要調用自己內置的__iter__方法),所以生成器就是可叠代對象
生成器分類及在python中的表現形式:(Python有兩種不同的方式提供生成器)
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
為何使用生成器之生成器的優點
Python使用生成器對延遲操作提供了支持。所謂延遲操作,是指在需要的時候才產生結果,而不是立即產生結果。這也是生成器的主要好處。
生成器小結:
1.是可叠代對象
2.實現了延遲計算,省內存啊
3.生成器本質和其他的數據類型一樣,都是實現了叠代器協議,只不過生成器附加了一個延遲計算省內存的好處,其余的可叠代對象可沒有這點好處,記住嘍!!!
六 生成器函數七 生成器表達式和列表解析
#三元表達式name=‘alex‘
name=‘linhaifeng‘ res=‘SB‘ if name == ‘alex‘ else ‘shuai‘print(res)
總結:
1.把列表解析的[]換成()得到的就是生成器表達式
2.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
3.Python不但使用叠代器協議,讓for循環變得更加通用。大部分內置函數,也是使用叠代器協議訪問對象的。例如, sum函數是Python的內置函數,該函數使用叠代器協議訪問對象,而生成器實現了叠代器協議,所以,我們可以直接這樣計算一系列值的和:
1 sum(x ** 2 for x in xrange(4))
而不用多此一舉的先構造一個列表:
1 sum([x ** 2 for x in xrange(4)])
八 生成器總結
綜上已經對生成器有了一定的認識,下面我們以生成器函數為例進行總結
- 語法上和函數類似:生成器函數和常規函數幾乎是一樣的。它們都是使用def語句進行定義,差別在於,生成器使用yield語句返回一個值,而常規函數使用return語句返回一個值
- 自動實現叠代器協議:對於生成器,Python會自動實現叠代器協議,以便應用到叠代背景中(如for循環,sum函數)。由於生成器自動實現了叠代器協議,所以,我們可以調用它的next方法,並且,在沒有值可以返回的時候,生成器自動產生StopIteration異常
- 狀態掛起:生成器使用yield語句返回一個值。yield語句掛起該生成器函數的狀態,保留足夠的信息,以便之後從它離開的地方繼續執行
優點一:生成器的好處是延遲計算,一次返回一個結果。也就是說,它不會一次生成所有的結果,這對於大數據量處理,將會非常有用。
1 #列表解析2 sum([i for i in range(100000000)])#內存占用大,機器容易卡死3 4 #生成器表達式5 sum(i for i in range(100000000))#幾乎不占內存優點二:生成器還能有效提高代碼可讀性
這裏,至少有兩個充分的理由說明 ,使用生成器比不使用生成器代碼更加清晰:
- 使用生成器以後,代碼行數更少。大家要記住,如果想把代碼寫的Pythonic,在保證代碼可讀性的前提下,代碼行數越少越好
- 不使用生成器的時候,對於每次結果,我們首先看到的是result.append(index),其次,才是index。也就是說,我們每次看到的是一個列表的append操作,只是append的是我們想要的結果。使用生成器的時候,直接yield index,少了列表append操作的幹擾,我們一眼就能夠看出,代碼是要返回index。
生成器,叠代器