一、生成器
1,生成器基本概念
生成器的實質是迭代器
迭代器:Iterator 內部同時包含了__iter__()和__next__()函式 可迭代物件:Iterable 內部包含__iter__()函式 迭代器的特點:(同時也是生成器特點) 1,節省記憶體 2,惰性機制 3,不能反覆只能向下執行
在Python中有三種方法來獲取生成器:
1. 通過生成器函式
2. 通過各種推導式來實現生成器
3.通過資料的轉換也可以獲取生成器
示例:先看一個簡單的函式:
def func(): print("111") return 222 ret = func() print(ret) 結果: 111 222
再將函式中的return換成yield變成生成器函式:
def func(): print("111") yield 222 ret = func() print(ret) 結果: <generator object func at 0x10647a8e0>
可以看出結果是不一樣的。由於函式中存在了yield,那麼這個函式就是一個生成器函式。我們再執行這個函式的時候,就不再是函式的執行了,而是獲取這個生成器。
因為生成器的本質是迭代器,所以我們可以直接執行__next__()以下生成器:
def func(): print("111") yield 222 gener = func() # 這個時候函式不會執行,只是獲取到生成器 ret = gener.__next__() # 函式執行,yield和return的作用一樣,返回資料 print(ret) 結果: 111 222
所以,yield和return的效果是一樣的,都是返回資料,但還是有很大區別的:
yield是分段來執行一個函式;return是直接停止執行函式
2,生成器的send()方法:
send()和__next__()一樣都可以讓生成器執行到下一個yield
def eat(): print("meat") a = yield "milk" print(`baozi`, a) b = yield `zhou` print(`guobaorou`, b) yield `tea` gen = eat() ret = gen.__next__() print(ret) ret = gen.__next__() print(ret) ret = gen.send("MIANBAO") # 給上一個yield傳值 print(ret) ret = gen.send("DANGAO") # 給上一個yield傳值 print(ret) 結果: meat milk baozi None zhou guobaorou MIANBAO tea Traceback (most recent call last): ret = gen.send("DANGAO") # 給上一個yield傳值 StopIteration
send()和__next__():
1. send和next都是讓生成器向下走一次
2. send可以給上一個yield的位置傳遞值,不能給最後一個yield傳送值。也不能在第一次執行生成器程式碼的時候不能使用send()
3,使用for迴圈
def func(): yield "a" yield "b" yield "c" gen = func() for el in gen: print(el) # 結果: # a # b # c
使用for迴圈的話會獲取內部所有的元素
二、列表推導式,生成器表示式以及其他推導式
1,列表推導式
常用寫法:[結果 for迴圈 if篩選]
篩選模式:[結果 for 變數 in 可迭代物件 if 條件]
練習題:
# (1)建立列表:[1,3,5,7,9,...99] l2 = [i for i in range(1,100) if i % 2 == 1] # [結果 for迴圈 if篩選] print(l2) # [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99] # 列表推導式 # (1) 過濾掉長度小於3的字串列表,並將剩下的轉換成大寫字母 lst = [`hello`, `world`, `hh`, `old`, `boy`, `H`] l1 = [i.upper() for i in lst if len(i) >= 3] print(1, l1) # (2) 求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇陣列成的元祖列表 l2 = [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1] # l2 = list(zip([x for x in range(6) if x % 2 == 0], [y for y in range(6) if y % 2 == 1])) print(2, l2) # (3) 求M中3,6,9組成的列表M = [[1,2,3],[4,5,6],[7,8,9]] M = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] l3 = [el[2] for el in M] print(3, l3) # (4) 求出50以內能被3整除的數的平方,並放入到一個列表中。 l4 = [i*i for i in range(1, 50) if i % 3 == 0] print(4, l4) l4_1 = [i for i in range(1, 50) if i % 3 == 0 and i*i < 50] print(l4_1) # (6) 構建一個列表:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)] # l6 = [(x, y) for x in range(6) for y in range(1, 7)] l6 = [(x, x+1) for x in range(6)] print(6, l6) # (7) 構建一個列表:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] l7 = [i for i in range(0, 19, 2)] print(7, l7) # (8)有一個列表l1 = [`alex`, `WuSir`, `老男孩`, `太白`]將其構造成這種列表[`alex0`, `WuSir1`, `老男孩2`, `太白3`] ll = [`alex`, `WuSir`, `老男孩`, `太白`] l8 = [v+str(i) for i, v in enumerate(ll)] print(8, l8) # l8 = [] # for i,v in enumerate(ll): # v = v+str(i) # print(v,i) # (9)有以下資料型別: x = { `name`: `alex`, `Values`: [ {`timestamp`:1517991992.94, `values`:100,}, {`timestamp`: 1517992000.94, `values`: 200,}, {`timestamp`: 1517992014.94, `values`: 300,}, {`timestamp`: 1517992744.94, `values`: 350}, {`timestamp`: 1517992800.94, `values`: 280} ], } # 將上面的資料通過列表推導式轉換成下面的型別: # [[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]] l9 = [[el[`timestamp`], el[`values`]] for el in x[`Values`]] print(9, l9) # 結果: 1 [`HELLO`, `WORLD`, `OLD`, `BOY`] 2 [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)] 3 [3, 6, 9] 4 [9, 36, 81, 144, 225, 324, 441, 576, 729, 900, 1089, 1296, 1521, 1764, 2025, 2304] [3, 6] 6 [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)] 7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] 8 [`alex0`, `WuSir1`, `老男孩2`, `太白3`] 9 [[1517991992.94, 100], [1517992000.94, 200], [1517992014.94, 300], [1517992744.94, 350], [1517992800.94, 280]]
# (5) 尋找名字中帶有兩個e的人的名字 names = [[`Tom`, `Billy`, `Jefferson`, `Andrew`, `Wesley`, `Steven`,`Joe`], [`Alice`, `Jill`, `Ana`, `Wendy`, `Jennifer`, `Sherry`, `Eva`]] # l5 = [] # for i in range(len(names)): # for j in names[i]: # if j.count(`e`) >= 2: # l5.append(j) l5 = [j for i in range(len(names)) for j in names[i] if j.count(`e`) >= 2] print(l5) # 主要注意str.count()的運用,查詢字串數量
2,生成器表示式:
語法與列表推導式基本上是一樣的。只是把 [] 替換成 {}
gen = (i for i in range(10)) # 生成表示式 print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) print(gen.__next__()) # print(gen.__next__()) # 超出數量會報錯 # 結果: # 0 # 1 # 2 # 3 # 4 # 5 # 6 # 7 # 8 # 9
生成器表示式與列表推導式的區別:
1,列表推導式比較耗記憶體,一次性載入;而生成器表示式幾乎不佔用記憶體,使用的時候才分配和使用記憶體。
2,得到的值不一樣:列表推導式得到的是一個列表,生成器表示式獲得的是一個生成器。
生成器的惰性求值:生成器只有在訪問的時候才取值。