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函数
作业:
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 # 来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作