生成器
函式體內有yield選項的就是生成器,生成器的本質是迭代器,由於函式結構和生成器結構類似,可以通過呼叫判斷是函式還是生成器.如下:
def fun(): yield "我是生成器" print(fun()) # 列印內容如下: <generator object fun at 0x0000000002160ED0>
生成器的優點就是節省記憶體.
Python獲取生成器的二種方式:
- 通過函式獲取生成器
- 通過生成器推導式建立生成器
通過函式獲取生成器
def fun(): print("fun") yield "生成器" g = fun() print(g) # 列印函式名檢視是否是生成器 # 列印內容如下: <generator object fun at 0x0000000000510ED0>
從列印內容可以看出是生成器.但是發現生成器裡面的內容沒有被列印,那如何列印生成器內容呢?我們可以把生成器理解成迭代器的變異版,所以要列印生成器的內容,與迭代器類似,建立生成器物件後.可以使用生成器.__next__()來列印生成器內容.或者next(),send()等來列印生成器,如下:
使用.__next__()來列印生成器中的內容
def fun(): print("fun") yield "生成器" print("我在生成器的下面") g = fun() # 建立生成器物件 print(g) # 列印生成器物件 print(g.__next__()) # 列印生成器裡面的內容 # 列印內容如下: <generator object fun at 0x0000000002200ED0> fun 生成器
可以發現yield下面的print語句沒有被列印.到yield停止了
def fun(): print("fun") yield "生成器1" print("我在生成器1下面") yield "生成器2" print("我在生成器2的下面") g = fun() # 建立生成器物件 print(g.__next__()) print(g.__next__()) # 列印內容如下: fun 生成器1 我在生成器1下面 生成器2
由上面兩個事例可以得出一個總結:就是每next一次就執行一次yield上面的程式碼一次,yield下面的程式碼不會被執行,這就是生成器的惰性機制
使用next()列印生成器內容
def fun(): print("fun") yield "生成器" print("我在生成器下面") yield "生成器2" print("我在生成器2的下面") g = fun() print(next(g)) # next(g)列印生成器內容 print(next(g)) # next(g)列印生成器內容 # 列印內容如下: fun 生成器 我在生成器下面 生成器2
與.__next__()功能類似
使用send(引數)列印生成器內容:
send方法可以給上一層的yield傳遞一個值,如果上一個yield沒有值的話send的引數將被忽略,如果有值yield的值將被改變成當前的引數,還有需要注意的地方就是如果send(引數)做為第一次迭代,由於上一層沒有yield,所以沒有辦法傳參,會導致出現錯誤,錯誤內容如下:
TypeError: can`t send non-None value to a just-started generator
我們將send(None)作為第一次呼叫即可.然後在第二次呼叫時可以傳適當的引數.
如下:
def fun(): print("fun") val = yield "生成器" print("我在生成器下面") print(val) yield "生成器2" print("我在生成器2的下面") yield "生成器3" print("我在生成器3的下面") g = fun() print(g.send(None)) print(g.send("send")) print(g.send("send2")) # 列印內容如下: fun 生成器 我在生成器下面 send 生成器2 我在生成器2的下面 生成器3
生成器的基礎用法:
使用for迴圈列印生成器物件
def fun(): print("fun") yield "生成器" print("我在生成器下面") yield "生成器2" print("我在生成器2的下面") yield "生成器3" print("我在生成器3的下面") g = fun() # 建立生成器物件 for g_buf in g: # 使用for迴圈列印生成器物件 print(g_buf) # 列印內容如下 fun 生成器 我在生成器下面 生成器2 我在生成器2的下面 生成器3 我在生成器3的下面
yield可以返回任何資料型別,這裡以列表為事例
def fun(): list_1 = [1,2,3,4,5] yield list_1 # 將整個列表作為返回值傳給生成器物件 g = fun() # 建立生成器物件 print(g.__next__()) # 列印生成器物件 # 列印內容如下: [1, 2, 3, 4, 5]
如果想要yield從列表中每次返回一個元素使用yield from 列表來實現
def fun(): list_1 = [1,2,3,4,5] yield from list_1 g = fun() # 建立生成器物件 print(g.__next__()) # 列印生成器物件內容 # 列印內容如下: 1
可以發現只列印了列表中的一個元素.可以使用for迴圈列印所有內容:
def fun(): list_1 = [1,2,3,4,5] yield from list_1 g = fun() for g_buf in g: print(g_buf) # 列印內容如下: 1 2 3 4 5
相當於事項了5次print(g.__next__()) # 列印生成器物件內容
推導式:
列表推導式:
如給list_1列表賦值1-20,常規做法如下:
list_1 = [] for num in range(20): list_1.append(num) print(list_1) # 列印內容如下: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
列表list_1和list_2簡單的推導式如下:
list_1 = [num for num in range(20)] list_2 = ["Python: %s" % num for num in range(5)] print(list_1) print(list_2) # 列印內容如下: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] [`Python: 0`, `Python: 1`, `Python: 2`, `Python: 3`, `Python: 4`]
列表推導式還可以進行篩選,如下:
list_1 = [num for num in range(20) if num < 5 or num == 15] print(list_1) # 列印內容如下: [0, 1, 2, 3, 4, 15]
升級一點,將一個巢狀列表中以”a”開頭和以”h”開頭的元素存放在一個空列表中
基礎寫法如下:
names = [[`abc`, `abb`, `zzz`],["hello","world","xiaoming"]] list_names = [] for name_1 in names: if type(name_1) == list: for name_2 in name_1: if name_2.startswith("a") or name_2.startswith("h"): list_names.append(name_2) print(list_names) # 列印內容如下: [`abc`, `abb`, `hello`]
使用列表推導法
names = [[`abc`, `abb`, `zzz`],["hello","world","xiaoming"]] list_names = [name_2 for name_1 in names if type(name_1) for name_2 in name_1 if name_2.startswith("a") or name_2.startswith("h")] # 列印內容如下: [`abc`, `abb`, `hello`]
生成器推導式:
與列表推導式類似,只不過列表是使用[],生成器推導式使用的是()
g_1 = (num for num in range(20)) print(g_1) print(g_1.__next__()) print(g_1.__next__()) # 列印內容如下: <generator object <genexpr> at 0x00000000026A0ED0> 0 1
從列印內容和使用__next__()方法可以看出g_1是列表表示式.
可以使用for迴圈列印生成器物件
g_1 = (num for num in range(20)) for num in g_1: print(num)
生成器的篩選與列表推導式用法一樣,只不過是()
如下:過濾1-20內的所有偶數
g_1 = (num for num in range(20) if num % 2 == 0)
升級:與上面列表推導式升級練法類似.
names = [[`abc`, `abb`, `zzz`],["hello","world","xiaoming"]] list_names = (name_2 for name_1 in names if type(name_1) for name_2 in name_1 if name_2.startswith("a") or name_2.startswith("h")) # 建立生成器物件 print(list_names) for buf in list_names: print(buf) # 列印內容下: <generator object <genexpr> at 0x0000000002150ED0> abc abb hello
生成器表示式和列表推導式的區別:
- 列表推導式比較耗記憶體,一次性載入.生成器表示式幾乎不佔用記憶體.使用的時候才分配和使用記憶體
- 得到的值不一樣,列表推導式得到的是一個列表.生成器表示式獲取的是一個生成器
字典推導式:
list_1 = ["電視劇","電影"] list_2 = ["上海灘","黃飛鴻"] dict_1 = {list_1[i]:list_2[i] for i in range(len(list_1))} print(dict_1) # 列印內容如下: {`電視劇`: `上海灘`, `電影`: `黃飛鴻`}
集合推導式:
集合的特點;無序,不重複 所以集合推導式自帶去重功能
list_1 = [1,2,3,4,2,3,5] set_1 = {i for i in list_1} # 集合推導式 print(set_1) # 列印內容如下: {1, 2, 3, 4, 5}
總結:
- 推導式有列表推導式,生成器推導式,字典推導式,集合推導式
- 生成器表示式: (結果 for 變量 in 可迭代物件 if 條件篩選)
- 生成器表示式可以直接獲取到生成器物件,生成器物件具有惰性,每次只能列印一個生成器內容,可以使用for迴圈列印生成器所有的內容.