python協程函數應用 列表生成式 生成器表達式
一、知識點整理:
1、可叠代的:對象下有_iter_方法的都是可叠代的對象
叠代器:對象._iter_()得到的結果就是叠代器
叠代器的特性:
叠代器._next_() 取下一個值
優點:
1.提供了一種統一的叠代對象的方式,不依賴於索引
2.惰性計算
缺點:
1.無法獲取叠代器的長度
2.一次性的,只能往後取值,不能往前退,不能像索引那樣去取得某個位置的值
2、生成器:函數內帶有yield關鍵字,那麽這個函數執行的結果就是生成器
生成器的本質就是叠代器
def func():
n=0
while True:
yield n
n+=1
g = func()
next(g)
3、總結yield的功能:
1、相當於把_iter_和_next_方法封裝到函數內部
2、與return比,return只能返回一次,而yield能返回多次
3、函數暫停以及繼續運行的狀態是通過yield保存的
4、yield的表達式形式: 如:food = yield
def eater(name):
print("%s start to eat"%name)
while True:
food = yield
print("%s eat %s"%(name,food))
e = eater("zhejiangF4")
next(e)
e.send("aaa")
5、e.send 與 next(e)的區別
#1.如果函數內yield是表達式形式,那麽必須先next(e)
#2.二者的共同之處是都可以讓函數在上一次暫停的位置繼續運行,不一樣的地方在於send在觸發下一次代碼的執行時,會順便給yield傳一個值
二、生成器 協程函數的應用
1、編寫一個裝飾器,在不變更原代碼 協程函數 的基礎上,直接就能給主函數傳值!(協程函數第一步需要next()觸發,既將觸發寫入裝飾器中!)
1 def f(func): #定義裝飾器 2 def f1(*args,**kwargs): 3 res = func(*args,**kwargs) 4 next(res) #觸發主函數 5 return res 6 return f1 7 @f 8 def eater(name): #主函數 9 print("%s start to eat"%name) 10 while True: 11 food = yield 12 print("%s eat %s"%(name,food)) 13 e = eater("zhejiangF4") 14 e.send("something") #直接傳值
執行結果:
1 zhejiangF4 start to eat 2 zhejiangF4 eat something
##在IDE上加上斷點,debug運行查看##
2、遞歸目錄,過濾文件中帶有“python”內容的文件,然後將這些文件打印。此段代碼實現功能,牽扯到面向過程編程的思想!定義的每一個函數都是環環相扣,猶如一個完整的生產線一樣!
面向過程的編程思想:流水線式的編程思想,在設計程序時,需要把整個流程設計出來
#優點:
1、體系結構更加清晰
2、簡化程序的復雜度
#缺點:
1、可擴展性極其的差,所以說面向過程的應用場景是:不需要經常變化的軟件。
1 import os,time 2 def init(func): 3 def wrapper(*args,**kwargs): 4 res = func(*args,**kwargs) 5 next(res) 6 return res 7 return wrapper 8 9 @init 10 def search(target): 11 ‘找到文件的絕對路徑‘ 12 while True: 13 dir_name=yield 14 #print(‘車間search開始生產產品:文件的絕對路徑‘) 15 time.sleep(1) 16 g = os.walk(dir_name) 17 for i in g: 18 for j in i[-1]: 19 file_path = "%s\\%s"%(i[0],j) 20 target.send(file_path) 21 @init 22 def opener(target): 23 ‘打開文件,獲取文件句柄‘ 24 while True: 25 file_path = yield 26 #print(‘車間opener開始生產產品:文件句柄‘) 27 time.sleep(1) 28 with open(file_path) as f: 29 target.send((file_path,f)) 30 @init 31 def cat(target): 32 ‘讀取文件內容‘ 33 while True: 34 file_path,f = yield 35 #print(‘車間cat開始生產產品:文件的一行內容‘) 36 time.sleep(1) 37 for line in f: 38 target.send((file_path,line)) 39 @init 40 def grep(pattern,target): 41 ‘過濾一行內容中有無python‘ 42 while True: 43 file_path,line = yield 44 #print(‘車間grep開始生產產品:包含python這一行內容的文件路徑‘) 45 time.sleep(0.2) 46 if pattern in line: 47 target.send(file_path) 48 @init 49 def printer(): 50 ‘打印文件路徑‘ 51 while True: 52 file_path = yield 53 #print(‘車間printer開始生產產品:得到最終的產品‘) 54 time.sleep(1) 55 print(file_path) 56 g = search(opener(cat(grep(‘python‘,printer())))) 57 g.send(‘G:\\zhang‘)
執行結果:
1 G:\zhang\a3.txt 2 G:\zhang\a1\a1.txt 3 G:\zhang\a2\a2.txt
三、列表生成式
1、由來
在實際編程的情況中,我們常常需要生成一些列表。除了比較低效的用for循環來一個一個往列表中append外,另一個比較好的方法就是:
python給我們提供了非常強大的創建列表的方式。
2、語法
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
for item3 in iterable3 if condition3
for itemN in iterableN if conditionN]
通俗的來講,列表生成式由三部分拼接組成:當然每次寫之前都應該先給出[],然後在裏邊添加。
1.expression 指要生成的元素(參數,變量),放在最前面
2.後面跟上for循環
3.for循環之後還可以加上if條件判斷,以便進行篩選。
實際使用的過程中,若一個for循環不能完成問題,還可以往下嵌套。
1)簡單代碼舉例:
1 egg_list=[] 2 for i in range(10): 3 egg_list.append("egg%s"%i) 4 print(egg_list) 5 6 l=["egg%s"%i for i in range(10)] 7 print(l)
執行結果:
1 [‘egg0‘, ‘egg1‘, ‘egg2‘, ‘egg3‘, ‘egg4‘, ‘egg5‘, ‘egg6‘, ‘egg7‘, ‘egg8‘, ‘egg9‘] 2 [‘egg0‘, ‘egg1‘, ‘egg2‘, ‘egg3‘, ‘egg4‘, ‘egg5‘, ‘egg6‘, ‘egg7‘, ‘egg8‘, ‘egg9‘]
2)稍微有點復雜的,不過也好理解。
1 #將l 和 s 中每一個元素取出,組成一個新的元組,將所有的結果保存在列表中 2 l = [1,2,3,4] 3 s = "hello" 4 l1 = [(num,s1) for num in l if num >3 for s1 in s] 5 print(l1) 6 7 l2 = [] 8 for num1 in l : 9 if num1 >3: 10 for s2 in s : 11 t = (num1 ,s2) 12 l2.append(t) 13 print(l2)
執行結果:
1 [(4, ‘h‘), (4, ‘e‘), (4, ‘l‘), (4, ‘l‘), (4, ‘o‘)] 2 [(4, ‘h‘), (4, ‘e‘), (4, ‘l‘), (4, ‘l‘), (4, ‘o‘)]
通過比較,雖然上邊兩種方式都可以實現功能,但是可以非常明顯的看出:運用傳統意義上的循環,去編寫代碼是非常繁瑣復雜的。
而運用 列表生成式,同樣的內容,可以通過一個list快速生成實現功能的代碼,同時寫出的代碼非常簡潔。
3)再舉個例子:讀取文件的絕對路徑
①代碼:
1 import os 2 g = os.walk("G:\\zhang") #拿取文件路徑下所有的文件 3 #print(g) #g是一個生成器 4 l = [] 5 for i in g: #獲取所有文件的絕對路徑 6 #print(i) #路徑整體以元組的形式打印出來,元組內部是列表(文件路徑,文件名,文件) 7 for j in i[-1]: #拿取有文件的路徑 8 file_path = "%s\\%s" % (i[0], j) 9 l.append(file_path) 10 print(l) 11 12 g = os.walk("G:\\zhang") 13 l1 = ["%s\\%s" %(i[0], j) for i in g for j in i[-1]] 14 print(l1)
##如果不明白怎麽來的,可以將代碼拷出去,將print釋放,打印的結果即可!文件路徑可以隨意更改!##
②執行結果:
1 [‘G:\\zhang\\a3.txt‘, ‘G:\\zhang\\a1\\a1.txt‘, ‘G:\\zhang\\a2\\a2.txt‘]
四、生成器表達式
1、定義:
生成器表達式,我個人認為還不如叫列表生成器,就是把列表表達式改變了一下,變成了一個生成器。
而且這種改變非常簡單,就是把外[]換成了()就創建了一個generator。
通過列表生成式,我們可以直接創建一個列表。但受到內存的限制,列表容量肯定是有限的,同時那麽龐大的數據流,一下子拿出來什麽機器得卡的受不了。
而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出後續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。
在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
就昨天所學的生成器的理解來判斷:generator生成器保存的是算法,每次通過next()觸發取值,並且每次只取一個元素的值,直到計算到最後一個元素。
沒有更多的元素時,就會拋出StopIteration的錯誤。我們可以通過for循環來叠代它,並且不需要關心StopIteration的錯誤。
這種生成器經常運用於:處理文件,讀取數據庫中大量的數據 的情況之中。
1、簡單代碼舉例:
1 #還是下蛋的例子(……跟雞過不去了……) 2 l=[‘egg%s‘ %i for i in range(100)] 3 print(l) 4 5 g=l=(‘egg%s‘ %i for i in range(1000000000000000000000000000000000000)) 6 print(g) 7 print(next(g)) 8 print(next(g)) 9 for i in g: 10 print(i)
執行結果:
2、處理文件的代碼舉例:
1 #處理文件,去除文件中每行的空格 2 #傳統處理方式,如果數據很大的話,瞬間將內存擠爆…… 3 f=open(‘a.txt‘) 4 l=[] 5 6 for line in f: 7 line=line.strip() 8 l.append(line) 9 print(l) 10 11 f=open(‘a.txt‘) 12 f.seek(0) 13 l1=[line.strip() for line in f] 14 print(l1) 15 16 f=open(‘a.txt‘) 17 f.seek(0) 18 g=(line.strip() for line in f) 19 print(g) 20 print(next(g)) 21 22 23 #list(可叠代對象) 可以將叠代器轉換成列表 24 f=open(‘a.txt‘) 25 g=(line.strip() for line in f) 26 27 l=list(g) 28 print(l)
執行結果:View Code
3、應用:聲明式編程
1)求和函數 sum() 可以計算 可叠代的數據的值
1 #1、求和函數 sum() 可以計算 可叠代的數據的值 2 print(sum([1,2,3,4])) #直接對列表求和 3 nums_g=(i for i in range(3)) #生成器 4 print(sum(nums_g))#求和
執行結果:
1 10 2 3
2)計算購物清單總價
1 # 計算購物清單總價 2 # 1、傳統方式 3 money_l=[] 4 with open(‘b.txt‘) as f: 5 for line in f: 6 goods=line.split() #將文件中的每行以空格分割,然後以列表的形式保存 7 res=float(goods[-1])*float(goods[-2]) #求和 個數*單價 此處註意數據類型的轉換 str -> float 8 money_l.append(res) #生成一個總價的列表 9 print(money_l) #打印列表 10 print(sum(money_l))#求總價 11 # 12 # 2、列表生成器 方法 將上邊的代碼用聲明式編程代替 13 f=open(‘b.txt‘) 14 g=(float(line.split()[-1])*float(line.split()[-2]) for line in f) 15 print(sum(g)) 16 #
執行結果:
1 [30.0, 1000000.0, 6000.0, 90000.0, 30.0] 2 1096060.0 3 1096060.0
3)數據庫查詢的功能(文件數據,string)得到的內容是[{},{}]形式,列表套字典的形式。
1 res=[] 2 with open(‘b.txt‘) as f: 3 for line in f: 4 # print(line) 5 l=line.split() #把每行處理成列表 6 # print(l) 7 d={} #先定義一個空字典 8 d[‘name‘]=l[0] #往字典內賦值 9 d[‘price‘]=l[1] #往字典內賦值 10 d[‘count‘]=l[2] #往字典內賦值 11 res.append(d) #將新創建的字典寫到列表中 12 print(res) #打印結果 13 # 14 # 生成器表達式 方式 處理 15 with open(‘b.txt‘) as f: 16 res=(line.split() for line in f) #得到一個列表生成器 大列表,文件內所有內容都在 17 #print(res) #查看類型 生成器 18 dic_g=({‘name‘:i[0],‘price‘:i[1],‘count‘:i[2]} for i in res) #對叠代器進行取值,拿到每個小列表,組成一個新的字典,存放在新的列表中 19 print(dic_g)#查看類型 生成器 20 apple_dic=next(dic_g) #取第一值 前提是知道第一個是什麽 21 print(apple_dic[‘count‘])
執行結果:
1 [{‘name‘: ‘apple‘, ‘price‘: ‘10‘, ‘count‘: ‘3‘}, {‘name‘: ‘tesla‘, ‘price‘: ‘1000000‘, ‘count‘: ‘1‘}, {‘name‘: ‘mac‘, ‘price‘: ‘3000‘, ‘count‘: ‘2‘}, {‘name‘: ‘lenovo‘, ‘price‘: ‘30000‘, ‘count‘: ‘3‘}, {‘name‘: ‘chicken‘, ‘price‘: ‘10‘, ‘count‘: ‘3‘}] 2 <generator object <genexpr> at 0x00000000028EB360> 3 3
此處有一個非常有趣的問題,昨天所學,我們知道文件本身就是一個叠代器。
next()取值之後,會將文件關閉。往後就無法再取值,所以會有I/O錯誤 沒法讀取 文件關閉的報錯。
所以調用文件的話,建議用 f = open("b.txt") 或是next()觸發取值的話,縮近放在裏邊。
4)取出單價>10000 大體不變,只是將每行組成的列表,格式化 轉換成字典的時候進行過濾,取出滿足條件的內容
1 # 取出單價>10000 大體不變,只是將每行組成的列表,格式化 轉換成字典的時候進行過濾,取出滿足條件的內容 2 with open(‘b.txt‘) as f: 3 res=(line.split() for line in f) 4 # print(res) 5 dic_g=({‘name‘:i[0],‘price‘:i[1],‘count‘:i[2]} for i in res if float(i[1]) > 10000) 6 print(dic_g) 7 #print(list(dic_g)) #直接取值 8 for i in dic_g: #for循環取值 9 print(i)
執行結果:
1 <generator object <genexpr> at 0x00000000026BB3B8> 2 {‘name‘: ‘tesla‘, ‘price‘: ‘1000000‘, ‘count‘: ‘1‘} 3 {‘name‘: ‘lenovo‘, ‘price‘: ‘30000‘, ‘count‘: ‘3‘}
python協程函數應用 列表生成式 生成器表達式