一、推導式
推導式其實是循環語句一個簡寫,以用於快捷地生成列表、元組、字典和集合。例如:
lst1 = [x for x in range(5)]
lst2 = tuple([x for x in range(5)])
lst3 = {x:chr(65+x) for x in range(5)}
lst4 = {x for x in range(5)}
print(lst1,lst2,lst3,lst4)
# [0, 1, 2, 3, 4] (0, 1, 2, 3, 4) {0:‘A’, 1:‘B’, 2:‘C’, 3:‘D’, 4:‘E’} {0, 1, 2, 3, 4}
推導式第一部分是一個運算式,可以有函數,運算的結果就作爲序列的一個元素。不斷的循環,不斷地產生元素,所以元素構成一個序列。
1、推導式的後面部分是一個簡寫的循環語句,這個循環語句還可以嵌套。例如:
lst1 = [str(x)+’-’+str(y) for x in range(5) for y in range(2)]
lst2 = [str(x)+’-’+str(y) for x in range(5) for y in range(x)]
print(lst1,lst2,sep=’\n’)
# [‘0-0’, ‘0-1’, ‘1-0’, ‘1-1’, ‘2-0’, ‘2-1’, ‘3-0’, ‘3-1’, ‘4-0’, ‘4-1’]
# [‘1-0’, ‘2-0’, ‘2-1’, ‘3-0’, ‘3-1’, ‘3-2’, ‘4-0’, ‘4-1’, ‘4-2’, ‘4-3’]
2、在推導式的最後面還可以加一個 if 語句來過濾元素。例如:
lst1 = [x**2 for x in range(10) if x>=5]
lst2 = [str(x)+’-’+str(y) for x in range(5) for y in range(x) if x+y>3]
print(lst1,lst2,sep=’\n’)
# [25, 36, 49, 64, 81]
# [‘3-1’, ‘3-2’, ‘4-0’, ‘4-1’, ‘4-2’, ‘4-3’]
二、生成器
生成器從表面上看與推導列一樣,也是相當於循環語句的簡寫,只有在最外面限定只能用小括號,但生成器與推導式有兩點本質的不同:
一是生成器最後生成的不是序列,而是一個生成器迭代對象,當然這個生成器迭代對象可以輕易地轉換成列表、元組、字典和集合;
二是生成器在編譯時僅生成一個生成器對象,只在被調用時才創建生成器迭代對象。生成器對象相當於一臺機器,生成器迭代對象相當於產品。
舉例:
gen = (x for x in range(5)) # 最外面限定只能用小括號
print(gen) # 產生的是生成器迭代對象 <generator object at 0x00000000024CEDD0>
print(list(gen)) # 調用時會創建生成器迭代對象,生成器迭代對象很容易轉換成序列:[0,1,2,3,4]
推導列能用的循環嵌套和最後加一個 if 語句的方法,在生成器中一樣能用。
gen2 = (str(x)+’-’+str(y) for x in range(10) for y in range(x) if x+y>13)
print(list(gen2)) # [‘8-6’, ‘8-7’, ‘9-5’, ‘9-6’, ‘9-7’, ‘9-8’]
以上說的僅是用於生成序列的生成器,因爲它的外面用的是小括號,所以可稱之爲:元組型生成器。另外,當一個函數中有“yield”關鍵詞時,這個函數就變成了一個生成器型函數(又稱:函數型生成器)。
很多人將元組型生成器稱作“元組生成器”,我認爲這不是很妥當,因爲這很容易被誤認爲這個生成器是專門用於生成元組的,而其實不是,它最終生成的是一個生成器迭代對象,這個生成器迭代對象可以被轉換成元組,也可以被轉換成其它序列。
有 yield 的生成器型函數與無yield 的普通函數相比,區別還是很大的。
1、生成器型函數只有作爲next()參數被調用時纔會被執行,其它方式無法使用這種函數。
def fun01(): # 無 yield 的普通函數
print('++'); print('++++')
def fun02(): # 有 yield 的成器型函數
print('++'); yield 6; print('++++')
f02 = fun02()
f02 # 生成器型函數不運行,不顯示任何內容
print(f02) # f02 本身只是一個對象:<generator object fun02 at 0x0000000001D9EDD0>
f03 = fun02()
next(f03) # 生成器型函數只有被next()調用時纔會運行,顯示:++
2、生成器型函數在運行時,遇yield關鍵詞則返回其後的值,然後停止,例如:
f04 = fun02()
print(next(f04)) # 顯示如下:
++ # 這是遇yield之前的print('++')語句顯示的
6 # 返回yield關鍵詞後面的值,函數運行完yield這一句就停止
3、再次用next()調用生成器型函數時,函數不從第一句開始運行,而是從上一次停止的地方開始運行,即接着上次的繼續運行。運行到下一個yield關鍵詞那裏又停止。那問題是如果沒有了“下一個yield關鍵詞”呢?沒有了就拋出異常,一切停止。
f05 = fun02()
print(next(f05)) # 顯示如下:
++
6
print(next(f05)) # 顯示如下:
++++ # 上一次運行到yield這一句,這一次從這一句之後開始運行
StopIteration # 函數結束前但沒有遇見新的yield關鍵詞,則拋出異常
def fun03():
print('++'); yield 6
print('++++'); yield 7
print('++++++'); yield 8
print('++++++++')
f06 = fun03()
print(next(f06)) # 顯示如下:
++
6
print(next(f06)) # 顯示如下:
++++
7
print(next(f06)) # 顯示如下:
++++++
8
print(next(f06)) # 顯示如下:
++++++++
StopIteration # 函數結束前但沒有遇見新的yield關鍵詞,則拋出異常
4、由上可見,生成器型函數用着用着就會拋出異常,解決這個問題方法也很簡單,就是將yield關鍵詞放到一個無限循環中,這樣就可以無限調用了。另外,這種函數有遇見 yield 自動停止的功能,所以在無限循環中,也不用擔心會進入死循環。
def fun04(x=5):
while True: yield x ; x+=1
f07 = fun04(8)
print(next(f07),next(f07),next(f07),next(f07),next(f07),next(f07)) # 8 9 10 11 12 13
總之,生成器型函數猶如一支牙膏,不擠不出,擠一下出一點,擠完了就拋出異常。
三、解包
解包就是用一個簡潔的方式給多個變量賦值。等號左邊多個待賦值的變量用逗號“,”分隔,右邊一般是列表、元組等序列。例如:
x1,y1,z1 = [16,17,18]
x2,y2,z2 = (26,27,28)
x3,y3,z3 = dict({1:36,2:37,3:38}).items()
x4,y4,z4 = {46,47,48}
print(x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4) # 16 17 18 26 27 28 (1,36) (2,37) (3,38) 48 46 47
在for循環語句中也可以使用解包。例如:
for i,v,n in [(1,2,3),(11,12,13),(21,22,23)]:
print(i,v,n,end=' ; ') # 1 2 3 ; 11 12 13 ; 21 22 23 ;
———————————————— 本篇完 ————————————————
看完之後,麻煩您順手點擊下方 “點贊” 兩個字給我點個贊吧 ^-^ , 謝謝您了。
如果您還能像我小學一年級班主任好老師那樣,給我隨心寫上幾句表揚或批評的話語,那真是感激不盡!
在我人生的道路上,有了您的鼓勵和指導,我一定成長快快。