Python入門基礎知識點(生成器和推導式)
Python生成器
生成器的定義:生成器它的本質就是叠代器
我們知道的叠代器有兩種:一種是調用方法直接返回的,一種是可叠代對象通過執行iter方法得到的,叠代器有的好處是可以節省內存。
如果在某些情況下,我們也需要節省內存,就只能自己寫。我們自己寫的這個能實現叠代器功能的東西就叫生成器
在python中有以下幾種方式來獲取生成器
1.通過生成器函數
2.通過各種推到式來實現生成器
首先,我們先看一個很簡單的函數:
def func(): print(11) return 22 ret = func() print(ret) # 運行結果:11 22
我們只需要修改一個地方就可以把函數變成生成器 就是將函數中的return換成yield就是生成器
Python中提供的生成器:
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
生成器Generator:
本質:叠代器(所以自帶了__iter__方法和__next__方法,不需要我們去實現)
特點:惰性運算,開發者自定義
定義生成器
def func(): print(11) yield 22 ret = func() print(ret) # 運行結果: <generator object func at 0x000001A575163888>
一個包含yield關鍵字的函數就是一個生成器函數。yield可以為我們從函數中返回值,但是yield又不同於return,return的執行意味著程序的結束,調用生成器函數不會得到返回的具體的值,而是得到一個可叠代的對象。每一次獲取這個可叠代對象的值,就能推動函數的執行,獲取新的返回值。直到函數執行結束。
importtime def genrator_fun1(): a = 1 print(‘現在定義了a變量‘) yield a b = 2 print(‘現在又定義了b變量‘) yield b g1 = genrator_fun1() print(‘g1 : ‘,g1) #打印g1可以發現g1就是一個生成器 print(‘-‘*20) #我是華麗的分割線 print(next(g1)) time.sleep(1) #sleep一秒看清執行過程 print(next(g1))
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__()程序報錯 print(ret3) 結果: 111 222 333 444
當程序運行完最後一個yield,那麽後面繼續運行__next__()程序會報錯
好了生成器我們認識了,生成器有什麽作用呢?
生成器有什麽好處呢?就是不會一下子在內存中生成太多數據
假如我想讓工廠給學生做校服,生產2000000件衣服,我和工廠一說,工廠應該是先答應下來,然後再去生產,我可以一件一件的要,也可以根據學生一批一批的找工廠拿。
而不能是一說要生產2000000件衣服,工廠就先去做生產2000000件衣服,等回來做好了,學生都畢業了。。。
def produce(): """生產衣服""" for i in range(2000000): yield "生產了第%s件衣服"%i product_g = produce() print(product_g.__next__()) #要一件衣服 print(product_g.__next__()) #再要一件衣服 print(product_g.__next__()) #再要一件衣服 num = 0 for i in product_g: #要一批衣服,比如5件 print(i) num +=1 if num == 5: break
send:
def generator(): print(123) content = yield 1 print(‘=======‘,content) print(456) yield2 g = generator() ret = g.__next__() print(‘***‘,ret) ret = g.send(‘hello‘) #send的效果和next一樣 print(‘***‘,ret) #send 獲取下一個值的效果和next基本一致 #只是在獲取下一個值的時候,給上一yield的位置傳遞一個數據 #使用send的註意事項 # 第一次使用生成器的時候 是用next獲取下一個值 # 最後一個yield不能接受外部的值
yield from
yield from 在python3中提供一種可以直接把可叠代對象中的每一個數據作為生成器的結果進行返回
def func(): lst = [‘衛龍‘,‘老冰棍‘,‘北冰洋‘,‘牛羊配‘] yield from lst g = func() for i in g: print(i) 結果: 衛龍 老冰棍 北冰洋 牛羊配
有個小坑,yield from 是將列表中的每一個元素返回,所以 如果寫兩個yield from 並不會產生交替的效果
def func(): lst1 = [‘衛龍‘,‘老冰棍‘,‘北冰洋‘,‘牛羊配‘] lst2 = [‘饅頭‘,‘花卷‘,‘豆包‘,‘大餅‘] yield from lst1 yield from lst2 g = func() for i in g: print(i) 結果: 衛龍 老冰棍 北冰洋 牛羊配 饅頭 花卷 豆包 大餅
推導式:
列表推導式:
列表推導式,生成器表達式以及其他推導式,首先我們先看一下這樣的代碼,給出一個列表,通過循環,想列表中添加1~10
li = [] for i in range(10): li.append(i) print(li)
我們換成列表推導式是什麽樣的,來看看:
列表推導式的常?寫法:
[結果 for 變量 in 可叠代對象]
ls = [i for i in range(10)] print(ls)
列表推導式是通過?行來構建你要的列表, 列表推導式看起來代碼簡單. 但是出現錯誤之後很難排查.
lst = [‘python%s‘ % i for i in range(1,19)] print(lst)
篩選模式:
[結果 for 變量 in 可叠代對象 if 條件]
lst = [i for i in range(100) if i %2 == 0] print(lst)
生成器推導式:
生成器表達式和列表推導式的語法基本上一樣的,只是把[]換成()
gen = (i for i in range(10)) print(gen) # 結果: <generator object <genexpr> at 0x0000026046CAEBF8>
打印的結果就是一個生成器,我們可以使用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‘, ‘Joe‘], [‘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 i in gen: print(i)
生成器表達式和列表推導式的區別:
1.列表推導式比較耗內存,一次性加載.生成器表達式幾乎不占用內存.使用的時候才分配和使用內存
2.得到的值不一樣,列表推導式得到的是一個列表.生成器表達式獲取的是一個生成器
3..Python不但使用叠代器協議,讓for循環變得更加通用。大部分內置函數,也是使用叠代器協議訪問對象的。例如, sum函數是Python的內置函數,該函數使用叠代器協議訪問對象,而生成器實現了叠代器協議,所以,我們可以直接這樣計算一系列值的和:
sum(x ** 2 for x in range(4))
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 # list的底層有for循環,for就是一直執行__next__() 所以可以將生成器放到list中 print(list(g)) # 獲取g中的數據. 這時func()才會被執行. 打印111.獲取到222. g完畢. print(list(g1)) # 獲取g1中的數據. g1的數據來源是g. 但是g已經取完了. g1 也就沒有數據了 print(list(g2)) # 和g1同理理 print(next(g)) print(next(g1)) print(next(g2)) # 可以用next來驗證 其實list就是將內容叠代了轉換成了列表
字典推導式:
lst1 = [‘jay‘,‘jj‘,‘meet‘] lst2 = [‘周傑倫‘,‘林俊傑‘,‘郭寶元‘] dic = {lst1[i]:lst2[i] for i in range(len(lst1))} print(dic)
集合推導式:
集合推導式可以幫我們直接生成一個集合,集合的特點;無序,不重復 所以集合推導式自帶去重功能
lst = [1,2,3,-1,-3,-7,9] s = {abs(i) for i in lst} print(s)
總結:
? 推導式有, 列表推導式, 字典推導式, 集合推導式, 沒有元組推導式
? 生成器表達式: (結果 for 變量量 in 可叠代對象 if 條件篩選)
? 生成器表達式可以直接獲取到?成器對象. ?成器對象可以直接進行for循環. ?成器具有惰性機制.
? 集合推導式和字典推導式很是類似,記住一個小技巧能夠快速區分那個是字典那個是集合
? 字典推導式前面的結果是有個冒號,而集合的前面結果就是單純的結果
作業:
2. 用列表推導式做下列小題 Li = [1,23,4,5,6,’a’,’b’,’c’,’d’] a. 過濾掉長度小於3的字符串列表,並將剩下的轉換成大寫字母 b. 求(x,y)其中x是0-5之間的偶數,y是0-5之間的奇數組成的元祖列表 c. M = [[1,2,3],[4,5,6],[7,8,9]] 求M中3,6,9組成的列表 d. 求出50以內能被3整除的數的平方,並放入到一個列表中。 e. 構建一個列表:[‘python1期‘, ‘python2期‘, ‘python3期‘, ‘python4期‘, ‘python6期‘, ‘python7期‘, ‘python8期‘, ‘python9期‘, ‘python10期‘] f. 構建一個列表:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)] g. 構建一個列表:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18] h. 有一個列表l1 = [‘alex‘, ‘WuSir‘, ‘老男孩‘, ‘太白‘]將其構造成這種列表[‘alex0‘, ‘WuSir1‘, ‘老男孩2‘, ‘太白3‘] (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]]
Python入門基礎知識點(生成器和推導式)