Day 20 叠代器、生成器
一、 叠代器
1.叠代器協議是指:對象必須提供一個next方法,執行該方法要麽返回叠代中的下一項,要麽就引起一個StopIteration異常,以終止叠代 (只能往後走不能往前退)
2.可叠代對象:實現了叠代器協議的對象(如何實現:對象內部定義一個iter()方法)
3.協議是一種約定,可叠代對象實現了叠代器協議,python的內部工具(如for循環,sum,min,max函數等)使用叠代器協議訪問對象
二、python中for循環機制
for循環的本質:循環所有對象,全都是使用叠代器協議。
正本清源:
很多人會想,for循環的本質就是遵循叠代器協議去訪問對象,那麽for循環的對象肯定都是叠代器了啊,沒錯,那既然這樣,for循環可以遍歷(字符串,列表,元組,字典,集合,文件對象),那這些類型的數據肯定都是可叠代對象啊?但是,我他媽的為什麽定義一個列表l=[1,2,3,4]沒有l.next()方法。
(字符串,列表,元組,字典,集合,文件對象)這些都不是可叠代對象,只不過在for循環式,調用了他們內部的iter方法,把他們變成了可叠代對象
然後for循環調用可叠代對象的next方法去取值,而且for循環會捕捉StopIteration異常,以終止叠代
循環訪問方式
#for循環l本質就是遵循叠代器協議的訪問方式,先調用diedai_l=l.__iter__()方法,或者直接diedai_l=iter(l),然後依次執行diedai_l.next(),直到for循環捕捉到StopIteration終止循環
#for循環所有對象的本質都是一樣的原理
for循環
序列類型字符串,列表,元組都有下標,你用上述的方式訪問,perfect!但是你可曾想過非序列類型像字典,集合,文件對象的感受,for循環就是基於叠代器協議提供了一個統一的可以遍歷所有對象的方法,即在遍歷之前,先調用對象的iter方法將其轉換成一個叠代器,然後使用叠代器協議去實現循環訪問,這樣所有的對象就都可以通過for循環來遍歷了,而且你看到的效果也確實如此,這就是無所不能的for循環
生成器
什麽是生成器?
可以理解為一種數據類型,這種數據類型自動實現了叠代器協議(其他的數據類型需要調用自己內置的iter方法),所以生成器就是可叠代對象
生成器分類及在python中的表現形式:
(Python有兩種不同的方式提供生成器)
1.生成器函數:常規函數定義,但是,使用yield語句而不是return語句返回結果。yield語句一次返回一個結果,在每個結果中間,掛起函數的狀態,以便下次重它離開的地方繼續執行
2.生成器表達式:類似於列表推導,但是,生成器返回按需產生結果的一個對象,而不是一次構建一個結果列表
為何使用生成器之生成器的優點
(Python使用生成器對延遲操作提供了支持。所謂延遲操作,是指在需要的時候才產生結果,而不是立即產生結果。這也是生成器的主要好處。
生成器小結:
1.是可叠代對象
2.實現了延遲計算,省內存啊
3.生成器本質和其他的數據類型一樣,都是實現了叠代器協議,只不過生成器附加了一個延遲計算省內存的好處,其余的可叠代對象可沒有這點好處。
生成器函數
生成器表達式和列表解析
1.把列表解析的[]換成()得到的就是生成器表達式
2.列表解析與生成器表達式都是一種便利的編程方式,只不過生成器表達式更節省內存
3.Python不但使用叠代器協議,讓for循環變得更加通用。大部分內置函數,也是使用叠代器協議訪問對象的。例如, sum函數是Python的內置函數,該函數使用叠代器協議訪問對象,而生成器實現了叠代器協議,所以,我們可以直接這樣計算一系列值的和
生成器總結:
語法上和函數類似:生成器函數和常規函數幾乎是一樣的。它們都是使用def語句進行定義,差別在於,生成器使用yield語句返回一個值,而常規函數使用return語句返回一個值
自動實現叠代器協議:對於生成器,Python會自動實現叠代器協議,以便應用到叠代背景中(如for循環,sum函數)。由於生成器自動實現了叠代器協議,所以,我們可以調用它的next方法,並且,在沒有值可以返回的時候,生成器自動產生StopIteration異常
狀態掛起:生成器使用yield語句返回一個值。yield語句掛起該生成器函數的狀態,保留足夠的信息,以便之後從它離開的地方繼續執行。
優點一:生成器的好處是延遲計算,一次返回一個結果。也就是說,它不會一次生成所有的結果,這對於大數據量處理,將會非常有用。
優點二:生成器還能有效提高代碼可讀性。
這裏,至少有兩個充分的理由說明 ,使用生成器比不使用生成器代碼更加清晰:
使用生成器以後,代碼行數更少。大家要記住,如果想把代碼寫的Pythonic,在保證代碼可讀性的前提下,代碼行數越少越好
不使用生成器的時候,對於每次結果,我們首先看到的是result.append(index),其次,才是index。也就是說,我們每次看到的是一個列表的append操作,只是append的是我們想要的結果。使用生成器的時候,直接yield index,少了列表append操作的幹擾,我們一眼就能夠看出,代碼是要返回index。
這個例子充分說明了,合理使用生成器,能夠有效提高代碼可讀性。只要大家完全接受了生成器的概念,理解了yield語句和return語句一樣,也是返回一個值。那麽,就能夠理解為什麽使用生成器比不使用生成器要好,能夠理解使用生成器真的可以讓代碼變得清晰易懂。
註意事項:生成器只能遍歷一次
知識點程序:
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # __author__ = "DaChao" 4 # Date: 2017/6/15 5 6 ###################################################### 7 #叠代器 8 ###################################################### 9 10 # dic = {"a":1,"b":2,"c":3} 11 12 # aa = dic.__iter__() 13 # print(aa.__next__()) 14 # print(aa.__next__()) 15 # print(aa.__next__()) 16 # print(aa.__next__()) 17 18 # lit = [‘a‘,‘b‘,‘c‘] 19 # 20 # bb = lit.__iter__() 21 # print(bb.__next__()) 22 # print(bb.__next__()) 23 # print(bb.__next__()) 24 # print(bb.__next__()) 25 26 27 ###################################################### 28 #修飾器引進參數 Y 29 ###################################################### 30 31 # def deco(auth_type = "file"): 32 # def auth(func): 33 # def wrapper(*args,**kwargs): 34 # if auth_type == "file": 35 # print("文件的認證方式!") 36 # elif auth_type == "Idap": 37 # print("Idap的認證方式!") 38 # elif auth_type == "mysql": 39 # print("mysql的認證方式!") 40 # else: 41 # print("不知道的認證方式!") 42 # return wrapper 43 # return auth 44 # # 45 # @deco() #引進參數,如例,默認為auth_type="file" 46 # def index(): 47 # print("Welcome to index!") 48 # 49 # index() 50 51 ###################################################### 52 #制作緩存 53 ###################################################### 54 55 # import os 56 # from urllib.request import urlopen 57 # 58 # cache_path = r"D:\Python\study\Day 8\cache.txt" 59 # def cache(func): 60 # def wrapper(*args,**kwargs): 61 # if os.path.getsize(cache_path): #有緩存 62 # with open(cache_path,"rb") as f: 63 # res = f.read() 64 # else: 65 # res = func(*args,**kwargs) #下載 66 # with open(cache_path,"wb") as f: #制作緩存 67 # f.write(res) 68 # return res 69 # return wrapper 70 # 71 # @cache 72 # def get(url): 73 # ‘‘‘ 74 # 讀取網站並返回其內容 75 # :param url: 76 # :return: 77 # ‘‘‘ 78 # return urlopen(url).read() #註意指令,讀出內容並返回 79 # 80 # print(get("https://www.yinxiang.com/webclipper/guide/")) 81 82 ###################################################### 83 #從數據庫讀取用戶名和密碼 Y 84 ###################################################### 85 86 # aaa_path = r"D:\Python\study\Day 8\aaa.txt" # r後面內容不轉義 87 # 88 # def check(func): 89 # def wrapper(*args,**kwargs): 90 # usrname = input("Please input your name: ").strip() 91 # echo = input("Please input your echo: ").strip() 92 # with open(aaa_path,"r",encoding="utf-8") as f: 93 # usr_dic = eval(f.read()) 94 # if usrname in usr_dic and echo == usr_dic[usrname]: 95 # print("Loggin successful!") 96 # func(*args,**kwargs) 97 # else: 98 # print("Loggin error!") 99 # return wrapper 100 # 101 # @check 102 # # def index(): 103 # # print("Welcome to my world!") 104 # 105 # # index() 106 # def index(name): 107 # print("Welcome to %s`s world!" %name) 108 # 109 # index("dachao") 110 111 112 ###################################################### 113 #文件模擬數據存取 Y 114 ###################################################### 115 116 # usr_dic = { 117 # "egon":"123", 118 # "alex":"alex3714", 119 # "wupeiqi":"321", 120 # "yuanhao":"123123" 121 # } 122 # 123 # with open("aaa.txt","w",encoding="utf-8") as f: 124 # f.write(str(usr_dic)) #write 只能寫入字符串 125 # 126 # with open("aaa.txt","r",encoding="utf-8") as f: 127 # res = f.read() 128 # # print(res,type(res)) 129 # usr_dic=eval(res) 130 # print(usr_dic,type(usr_dic)) 131 132 133 134 ###################################################### 135 #eval功能:提取字符串命令並執行結果 Y 136 ###################################################### 137 # eval("print(‘hello world‘)") 138 139 140 141 ###################################################### 142 #註釋修改功能 Y 143 ###################################################### 144 # from functools import wraps #調用內部函數 wraps 145 # 146 # def check_xiushi(func): 147 # ‘‘‘ 148 # 修飾器:check_xiushi 149 # :param func: 150 # :return: 151 # ‘‘‘ 152 # @wraps(func) #必須在閉包上面,實現保留原始func功能的作用 153 # def wrapper(): 154 # ‘‘‘ 155 # 修飾器:閉包函數。 156 # :return: 157 # ‘‘‘ 158 # print("我是修飾器!") 159 # func() 160 # # wrapper.__doc__ = func.__doc__ #在調用階段,實現 161 # return wrapper 162 # 163 # @check_xiushi 164 # def check(): 165 # ‘‘‘ 166 # check function 167 # :return: 168 # ‘‘‘ 169 # print("Hello world!") 170 171 # check() 172 # print(check.__doc__) 173 # print(help(check)) 174 175 176 # print(check.__name__) 177 # check.__wrapped__() #調用wraps後,執行原始check函數knowledge
作業:
1 #! /usr/bin/env python 2 # -*- coding: utf-8 -*- 3 # __author__ = "DaChao" 4 # Date: 2017/6/15 5 6 # 四:編寫裝飾器,為多個函數加上認證的功能(用戶的賬號密碼來源於文件),要求登錄成功一次,後續的函數都無需再輸入用戶名和密碼 7 # 註意:從文件中讀出字符串形式的字典,可以用eval(‘{"name":"egon","password":"123"}‘)轉成字典格式 8 9 # cache_path = r"D:\Python\study\Day 8\cache.txt" 10 # 11 # login_dic = { 12 # "usr":None, 13 # "status":False 14 # } 15 # 16 # 17 # def check(func): 18 # ‘‘‘ 19 # 用戶登錄驗證,一次成功,後期不需要再次驗證! 20 # :param func: 21 # :return: 22 # ‘‘‘ 23 # def wrapper(): 24 # if login_dic["usr"] and login_dic["status"]: 25 # res = func() 26 # return res 27 # usrname = input("Please input your name: ") 28 # echo = input("Please input your echo: ") 29 # with open(cache_path,"r",encoding="utf-8") as f: 30 # usr_dic = eval(f.read()) 31 # if usrname in usr_dic and echo == usr_dic[usrname]: 32 # login_dic["usr"] = usrname 33 # login_dic["status"] = True 34 # print("Loggin successful!") 35 # func() 36 # else: 37 # print("Loggin err!") 38 # return wrapper 39 # 40 # @check 41 # def index(): 42 # print("Welcome to my world!") 43 # @check 44 # def index1(): 45 # print("hahahaha") 46 47 # 五:編寫下載網頁內容的函數,要求功能是:用戶傳入一個url,函數返回下載頁面的結果 48 # 六:為題目五編寫裝飾器,實現緩存網頁內容的功能: 49 # 具體:實現下載的頁面存放於文件中,如果文件內有值(文件大小不為0),就優先從文件中讀取網頁內容, 50 # 否則,就去下載,然後存到文件中 51 52 # import os 53 # from urllib.request import urlopen 54 # 55 # cache_path = r"D:\Python\study\Day 8\cache.txt" 56 # def cache(func): 57 # def wrapper(*args,**kwargs): 58 # if os.path.getsize(cache_path): #有緩存 59 # with open(cache_path,"rb") as f: 60 # res = f.read() 61 # else: 62 # res = func(*args,**kwargs) #下載 63 # with open(cache_path,"wb") as f: #制作緩存 64 # f.write(res) 65 # return res 66 # return wrapper 67 # 68 # @cache 69 # def get(url): 70 # ‘‘‘ 71 # 讀取網站並返回其內容 72 # :param url: 73 # :return: 74 # ‘‘‘ 75 # return urlopen(url).read() #註意指令,讀出內容並返回 76 # 77 # print(get("https://www.yinxiang.com/webclipper/guide/")) 78 79 # 七:還記得我們用函數對象的概念,制作一個函數字典的操作嗎, 80 # 來來來,我們有更高大上的做法,在文件開頭聲明一個空字典,然後在每個函數前加上裝飾器,完成自動添加到字典的操作work
Day 20 叠代器、生成器