一.生成器python
概念:生成器的是實質就是迭代器面試
1.生成器的貼點和迭代器同樣,取值方式也和迭代器同樣.app
2.生成器通常由生成器函數或者聲稱其表達式來建立,生成器其實就是手寫的迭代器.函數
3.在python中有三種方式來獲取生成器:spa
(1)經過生成器函數獲取生成器指針
(2)經過各類推導式來實現生成器;code
(3)經過生成器表達式來建立生成器對象
二.生成器函數blog
首先,先看一個簡單函數,以下:內存
def jaun(): print("111") return 222 ret = juan() print(ret) # 結果爲: # 111 # 222
將上面函數中的return換成yield就是生成器,以下:
def juan(): print("111") yield 222 ret = juan() print(ret) # 結果爲:<generator object func at 0x0000000002793CA8>
因爲函數中存在了yield,那麼這個函數就是一個生成器函數.這時,再執行這個函數的時候,就再也不在是函數的執行了,而是獲取這個生成器.
生成器的本質是迭代器,因此,咱們能夠直接執行__next__()來執行一下生成器
def juan(): print("111") yield 222 enner = juan() #這個時候函數不會執行,而是獲取到生成器 ret = genner.__next__() #這個時候纔會執行,yield的做用和return同樣,也是返回數據 print(ret) # 結果爲: # 111 # 222
yield 和 return的區別:
yield是分段來執行一個函數,return是直接中止執行函數
當程序運行完最後一個yield,那麼後面繼續進行__next__()程序會報錯,具體以下
def juan(): print("111") yield 222 print("222") yield 444 gener = func() ret = gener.__next__() print(ret) ret2=gener.__next__() print(ret2) ret3=gener.__next__() print(ret3) # 結果爲: # 111 # Traceback (most recent call last): # 222 # File "E:/pythonDemo/1-basis/test13.py", line 50, in <module> # 222 # 444 # ret3 = gener.__next__() # StopIteration
講到這裏.那生成器有什麼做用呢?咱們來看這樣一個需求,學校向JACK JONES訂購10000套學生服,JACK JONES就比較實在,直接造出來10000套衣服,以下代碼:
def cloth(): lst = [] for i in range(10000): lst.append("衣服"+str(i)) return lst cl = cloth()
可是呢,問題來了,學校如今沒有這麼多學生,一次性給學校這麼多,該往哪裏放,很尷尬啊!最好的效果是什麼樣呢?我要1套,你給我1套,一共10000套,是否是最完美的。以下代碼:
def cloth(): for i in range(10000): yield "衣服"+str(i) cl = cloth() print(cl.__next__()) print(cl.__next__()) print(cl.__next__()) print(cl.__next__())
分析:第一種是直接一次性所有拿出來,會很佔用內存,第二種是使用生成器,一次就一個,
用多少生成多少,生成器是一個一個的指向下一個,不會回去,__next__()到哪,指針就指到哪兒,下一次繼續去指針指向的值.
接下來咱們來看send()方法,send和__next__()同樣均可以讓生成器執行到下一個yield,以下代碼:
def eat(): print("我吃什麼啊") a = yield "饅頭" print("a=",a) b = yield "大餅" print("b=",b) c = yield "韭菜盒子" print("c=",c) yield "GAME OVER" gen = eat() # 獲取生成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send("胡辣湯") print(ret2) ret3 = gen.send("狗糧") print(ret3) ret4 = gen.send("貓糧") print(ret4) #我吃什麼啊 #饅頭 #a= 胡辣湯 #大餅 #b= 狗糧 #韭菜盒子 #c= 貓糧 #GAME OVER
send()和__next__()的區別:
1.send()和__next__()都是讓生成器向下走一次;
2.send()能夠給上一個yield的位置傳遞值,不能給最後一個yield發送值,在第一次執行生成器代碼的時候不能使用send()。
生成器能夠使用for循環來循環獲取內部的元素:
def func(): print(111) yield 222 print(333) yield 444 print(555) yield 666 gen = func() for i in gen: print(i) # 結果爲: # 111 # 222 # 333 # 444 # 555 # 666
三,推導式、生成器表達式
首先咱們先看一下這樣的代碼,給出一個列表,經過循環,向列表中添加1-13:,代碼以下:
lst = [] for i in range(1,14): lst.append(i) print(lst)
將上面替換成列表推導式,以下:
lst = [i for i in range(1,14)] print(lst)
列表推導式是經過一行來構建你要的列表,列表推導式看起來代碼簡單,可是出現錯誤以後很難排查。
列表推導式的經常使用寫法:[結果 for 變量 in 可迭代對象]
例:從python1期到python15期寫入列表lst,代碼以下:
lst = ["python%s" % i for i in range(1,16)] print(lst)
咱們還能夠對列表中的數據進行篩選:篩選模式:[結果 for 變量 in 可迭代對象 if條件],具體以下例:
# 獲取1-100內全部偶數 lst = [i for i in range(1,101) if i % 2 == 0] print(lst
生成器表達式和列表推導式的語法基本上是同樣的,只是把[]替換成(),具體以下:
gen = (i for i in range(10)) print(gen) # 結果爲:<generator object <genexpr> at 0x00000000021F3E60>
能夠看到打印的結果就是一個生成器,咱們能夠使用for循環來循環這個生成器,具體以下:
gen = ("數碼寶貝我第%s次愛你" % i for i in range(10)) for i in gen: print(i)
生成器表達式也能夠進行篩選,具體以下:
# 獲取1-100內能被3整除的數 gen = (i for i in range(1,100) if i % 3 == 0) for num in gen: print(num) # 100之內能被3整除的數的平方 gen = (i * i for i in range(100) if i % 3 == 0) for num in gen: print(num) # 尋找名字中帶有兩個e的人的名字 names = [ ['Tom','Billy','Jefferson','Andrew','Wesley','Steven'], ['Alice','Jill','Ana','Wendy','Jennifer','Sherry','Eva'] ] # 不用推導式和表達式 result = [] for first in names: for name in first: if name.count("e") == 2: result.append(name) print(result) # 生成器表達式 gen = (name for first in names for name in first if name.count("e") == 2) for name in gen: print(name)
總結:
生成器表達式和列表推導式的區別:
1,列表推導式比較耗內存,一次性加載,生成器表達式幾乎不佔用內存,使用的時候才分配和使用內存;
2,獲得的值不同,列表推導式獲得的是一個列表,生成器表達式獲取的是一個生成器;
舉個栗子:一樣一籃子雞蛋,列表推導式:直接拿到一籃子雞蛋;生成器表達式:拿到一個老母雞,須要雞蛋就給你下雞蛋。
生成器的惰性機制:生成器只有在訪問的時候才取值,說白了,你找他要他纔給你值,不找他要,他是不會執行的。
def func(): print(111) yield 222 g = func() # 生成器g g1 = (i for i in g) # 生成器g1,可是g1的數據來源於g g2 = (i for i in g1) # 生成器g2,數據來源於g1 print(list(g)) # 獲取g中的數據,這是func()纔會被執行,打印111,獲取到222,g完畢 print(list(g1)) # 獲取g1中的數據,g1的數據來源是g,可是g已經取完了,g1也就沒有數據了 print(list(g2)) # 和g1同理
深坑:生成器,要值的時候纔拿值!
字典推導式:根據名字應該也能猜到,推導出來的是字典,具體以下:
# 把字典中的key和value互換 dic = {'a':1,"b":2} new_dic = {v:k for k,v in dic.items()} print(new_dic) # 在如下list中,從lst1中獲取的數據和lst2中相對應的位置的數據組成一個新字典 lst1 = ['jay','jj','sylar'] lst2 = ['周杰倫','林俊杰','邱彥濤'] dic = {lst1[i]:lst2[i] for i in range(len(lst1))} print(dic)
集合推導式:集合推導式能夠幫咱們直接生成一個集合,集合有無序,不重複的特色,因此集合推導式自帶去重功能。具體以下代碼:
lst = [1,-1,8,-8,12] # 絕對值去重 s = {abs(i) for i in lst} print(s)
總結:
推導式有:
列表推導式:[結果 for 變量 in 可迭代對象 if 條件篩選]
字典推導式:{k:v for 變量 in 可迭代對象 if 條件篩選}
集合推導式:{k for 變量 in 可迭代對象 if 條件篩選}
沒有元組推導式
生成器表達式: (結果 for 變量 in 可迭代對象 if 條件篩選)
生成器表達式能夠直接獲取到生成器對象,生成器對象能夠直接進行for循環,生成器具備惰性機制。
看下面一個面試題(難度係數50000000顆星,友情提示:惰性機制,不到最後不會拿值):
def add(a,b): return a+b def test(): for r_i in range(4): yield r_i g = test() for n in [2,10]: g = (add(n,i) for i in g)
#過程
#1)
#n=2
#g1 = (add(n,i) for i in g)
#n=10
#g = (add(n,i) for i in g1)
----------------------------
#2)
#g = (add(n,i) for i in add(n,i) for i in g)
----------------------------------------
#3)
#g = (add(10,i) for i in add(10,i) for i in g)
[10,11,12,13]
結果:[20,21,22,23]
print(list(g))