Day-12 生成器
一、生成器
生成器實質就是迭代器
兩種方式寫生成器:
1.生成器函式
2.生成器表示式
首先,我們先看一個很簡單的函式:
def func(): print("111") return 222 ret = func() print(ret) 結果: 111 222
將函式中的return換成yield就是生成器
def func(): print("111") yield 222 gener = func() # 這個時候函式不會執行. 而是獲取到⽣成器 ret = gener.__next__() # 這個時候函式才會執行. yield的作用和return一樣. 也是返回 資料 print(ret) 結果: 111 222
yield和return的效果是一樣的,yield是分段來執行一個函式,return是直接停止執行函式。
def func(): print("111") yield 222 print("333") yield 444 gener = func() ret = gener.__next__() print(ret) ret2 = gener.__next__() print(ret2) ret3 = gener.__next__() # 最後一個yield執行完畢. 再次__next__()程式報錯, 也就是說. 和return無關了. print(ret3) 結果: 111 Traceback (most recent call last): 222 333 File "/Users/sylar/PycharmProjects/oldboy/iterator.py", line 55, in <module> 444 ret3 = gener.__next__() # 最後一個yield執行完畢. 再次__next__()程式報錯, 也就是說. 和return無關了. StopIteration
生成器函式有什麼用
普通的程式會佔用記憶體
def buy(): lst = [] for i in range(10000): lst.append("衣服%s" % i) return lst lst = buy() print(lst)
利用生成器,需要一件拿一件節省記憶體空間
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__()到哪,指標就知道哪兒,下一次繼續獲取指標指向的值。
特性:和迭代器一樣
1.節省記憶體
2.惰性機制
3.只能往前
生成器還可以使用__next__(), send()來訪問生成器
send()可以給上一個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)
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
二、列表推導式以及其他推導式
先看這樣的程式碼
lst = [] for i in range(1, 15): lst.append(i) print(lst)
替換成列表推導式:
lst = [i for i in range(1, 15)] print(lst)
列表推導式是通過一行來構建你要打的列表,列表推導式看起來程式碼簡單,但是出現錯誤之後很難排查。
列表推導式的常用寫法:[結果 for 變數 in 可迭代物件]
我們也可以對列表中的資料進行篩選
篩選模式:[結果 for 變數 in 可迭代物件 if 條件]
# 獲取1-100內所有的偶數 lst = [i for i in range(1, 100) if i % 2 == 0] print(lst)
字典推導式和集合推導式:[結果 for 變數 in 可迭代物件 if 條件]
s = {i*"胡辣湯" for i in range(10)} #集合推導式 print(s) dic = {"張無忌":"趙敏", "楊過":"小龍女", "郭靖":"黃蓉"} # 把字典中的key和value互換 # dic = {"趙敏":"張無忌", "小龍女":"楊過", "黃蓉":"郭靖"} dic1 = { v:k for k, v in dic.items()} # 強化 #字典推導式 print(dic1)
沒有元組推導式,因為元組沒有增刪改操作
三、生成器表示式
生成器表示式和列表推導式的語法基本上是一樣的. 只是把[]替換成()
gen = (i for i in range(10)) print(gen) 結果: <generator object <genexpr> at 0x106768f10>
列印的結果就是一個生成器,我們可以使用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)
生成器表示式和列表推導式的區別:
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同理
在Python3中提供了一種可以直接把可迭代物件中的每一個數據作為生成器的結果進行返回
def gen(): lst = ["麻花藤", "胡辣湯", "微星牌餅鐺", "Mac牌鍋鏟"] yield from lst g = gen() for el in g: print(el)
小坑: yield from是將列表中的每一個元素返回. 所以. 如果寫兩個yield from 並不會產生交替
的效果.
def gen(): lst = ["麻花藤", "胡辣湯", "微星牌餅鐺", "Mac牌鍋鏟"] lst2 = ["餅鐺還是微星的好", "聯想不能煮雞蛋", "微星就可以", "還可以烙餅"] yield from lst yield from lst2 g = gen() for el in g: print(el) 效果: 麻花藤 胡辣湯 微星牌餅鐺 Mac牌鍋鏟 餅鐺還是微星的好 聯想不能煮雞蛋 微星就可以 還可以烙餅
總結:推導式有,列表推導式,字典推導式,集合推導式,沒有元組推導式