生成器、列表推導式和生成器表達式
生成器
生成器特點:
1. 生成器本質上叠代器
2. 生成器是自己用代碼創建的
獲取生成器的兩種方式:
1. 生成器函數
2. 生成器表達式
yield
下面來看一個例子
1 def func(): 2 print(111) 3 yield 666 4 print(222) 5 yield 233333 6 7 8 ret = func() # 獲取生成器 9 print(ret) # <generator object func at 0x000002B1E535B258> 10 print(type(ret)) #<class ‘generator‘>
把函數的return換成yield就可以把函數變成生成器,變成生成器後再讀到函數名加括號後就不再表示執行函數而是表示獲取一個生成器。變成生成器後就可以用next來取值了,來看代碼
print(next(ret)) # 一個next執行一個yield(和yield之前的)
執行結果
111 666
再繼續取值
1 print(next(ret)) # 111 666, 一個next執行一個yield(和yield之前的) 2 print(next(ret)) # 222 233333 3 print(next(ret)) #超出,報錯
生成器函數牢記一點:一個next只執行一個yield語句和此yield語句與上一個yield語句之間的語句
如果將yield語句賦值給某個變量會怎樣,來看代碼
1 def func(): 2 print(111) 3 a = yield 666 4 print("a1:", a) 5 print(222) 6 yield 888 7 print("a2:", a) 8 yield 999 9 10 11 ret = func()
執行一個next
print(next(ret))
執行結果
111 666
執行兩個next
111
666
a1: None
222
888
執行三個next
print(next(ret)) print(next(ret)) print(next(ret))
結果
111 666 a1: None 222 888 a2: None 999
分析上述代碼我們可以得出規律:
1. yield語句是將yield後面的值返回給next()語句
2. yield語句本身沒有返回值
3. 每執行一個next語句就從上一個yield語句(不包括)開始從上往下執行
yield與return的異同:
1. return會終止函數,yield不會終止函數
2. yield和return都有返回值,yield給next()返回值,return給函數名()返回值
send
生成器函數還有一個方法:send(),send與next類似,也可以一個一個地取值,但是send還有個作用,可以傳值給上一個yield(括號裏寫None就跟next一樣)
1 def func(): 2 a = yield 111 3 print(a) 4 print("mandy") 5 b = yield 222 6 print(b) 7 print("sa") 8 yield 333 9 yield "hanser" 10 11 12 gen = func() # 獲取生成器 13 # sen不僅能對應yield取值,而且可以給上一個yield語句發送一個值 14 15 print(gen.send(None)) # 第一個send不能傳值(傳值為None,只取值) 111 16 print(gen.send(666)) # 兩個作用:1.把666傳給a;2.取值222. 666 mandy 222 17 print(gen.send("yousa")) # yousa sa 333 18 print(next(gen)) # hanser
執行結果
111 666 mandy 222 yousa sa 333 hanser
註意send(value)是將value傳給上一個yield的yield 值這個語句,如果函數裏有將yield 值賦值給某個變量,就要註意變量值的變化。代碼的執行順序是從上一個yield的後面開始,從上往下執行,最後執行當前的yield(next)。還有兩點點需要註意:
1. 第一次取值不能用send傳參
2. 最後一個yield永遠得不到send傳的值
yield from
前面的yield的返回值都是數字,如果yield的返回值是可叠代對象會有什麽不同呢,一起來看
1 def func(): 2 lst = [1, 2, 3, 4] 3 yield lst 4 5 6 gen = func() # gen [[1, 2, 3, 4]] 7 print(gen)
執行結果
<generator object func at 0x0000016A300AFF68>
[1, 2, 3, 4]
用next得到的結果是一個列表,如果不用next取值,直接用for循環取值,能取到嗎?來看
for i in next(gen): print(i)
結果
<generator object func at 0x0000024B4905FF68> 1 2 3 4
由此可以可以知道gen這個生成器類似於一個列表,裏面只有一個元素也是列表,即 [[1, 2, 3, 4]],
如果把yield換成yield from,結果會是怎樣的呢
1 def func(): 2 lst = [1, 2, 3, 4] 3 yield from lst # 把lst變成叠代器 4 5 6 gen = func() 7 print(next(gen)) 8 print(next(gen)) 9 print(next(gen))
執行結果
1 2 3
列表推導式
定義:列表推導式就是用一行代碼構建簡單的列表
列表推導式的三種模式
1. 循環模式
語法:[變量(加工後的變量) for 變量 in iterable]
[i for i in range(10)]
2. 篩選模式
語法:[變量(加工後的變量) for 變量 in iterable if 條件]
[i for i in range(10) if i % 2 == 0]
["地球%s號" % i for i in range(1, 50) if i % 3 == 0]
3. 三元模式(循環模式與三元運算的結合)
lst = ["$" if i % 2 == 0 else i for i in [i**2 for i in range(10)]]
print(lst)
執行結果
[‘$‘, 1, ‘$‘, 9, ‘$‘, 25, ‘$‘, 49, ‘$‘, 81]
優點:節省代碼
缺點:(1)只有一行,debug查不出來
(2)不能構建復雜的列表(常規的方法可以)
生成器表達式
生成器表達式語法與列表推導式幾乎一樣,只是把[]換成()
1 gen = ("嫦娥%s號" % i for i in range(1, 10)) 2 print(gen) 3 print(next(gen)) # 嫦娥1號 4 for i in gen: 5 print(i) # 嫦娥2號 嫦娥3號...
執行結果
<generator object <genexpr> at 0x0000016C3625FF68>
嫦娥1號
嫦娥2號
嫦娥3號
嫦娥4號
嫦娥5號
嫦娥6號
嫦娥7號
嫦娥8號
嫦娥9號
什麽時候用列表推導式,什麽時候用生成器表達式?
數據量小的時候用列表推導式,數據量大的時候用生成器表達式,因為生成器表達式可以一個一個地取值,不占內存
字典推導式和集合推導式
字典推導式
字典推導式的語法和列表推導式類似,只是把變量換成鍵值對,[]換成{}
print({i:None for i in range(1, 10)})
結果
{1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}
鍵值交換
# 鍵值交換 m = {"a": 10, "b": 34, "c": 88} print({v: k for k, v in m.items()})
執行結果
{10: ‘a‘, 34: ‘b‘, 88: ‘c‘}
集合推導式
set2 = {i*3+1 for i in range(10)} print(set2)
執行結果
set2 = {(i*3+1) % 4 for i in range(10)} print(set2)
執行結果
{0, 1, 2, 3}
生成器、列表推導式和生成器表達式