七.python迭代器&生成器&装饰器
1.迭代器
1.1 什么是可迭代对象(Iterable)?
-
定义:可以直接作用于for循环的对象统称为可迭代对象,即Iterable。
可迭代对象包括:
1.集合数据类型:如list、tuple、dict、set、str等;
2.生成器类型:包括生成器和带yield的生成器函数。
1.2 什么又是迭代器(Iterator)?
- 定义:可以被next()或__next__()函数调用并不断返回下一个值(直到没有数据时抛出StopIteration错误)的对象称为迭代器,即Iterator。
- 迭代器的生成: __iter__()生成迭代对象时调用
注意:可迭代对象和迭代器不是一个概念,不要混淆。
使用内置函数isinstance和collections测试:
1 >>> import collections 2 >>> print(isinstance([], collections.Iterable)) #判断列表是不是可迭代的 3 True 4 >>> print(isinstance([], collections.Iterator)) #判断列表是不是迭代器 5 False 6 >>> 7 >>> print(isinstance((), collections.Iterable)) #判断元组是不是可迭代的 8 True 9 >>> print(isinstance((), collections.Iterator)) #判断元组是不是迭代器 10 False 11 >>> 12 >>> print(isinstance({}, collections.Iterable)) #判断字典是不是可迭代的 13 True 14 >>> print(isinstance({}, collections.Iterator)) #判断元组是不是迭代器 15 False 16 >>>
以上表明,列表,字典,元组,等都是可迭代的,但都不是迭代器。
迭代器例子:
1 #因为列表不是迭代器,所以没有__next__方法 2 >>> a = [1,2,3,4] 3 >>> a.__next__() 4 Traceback (most recent call last): 5 File "<stdin>", line 1, in <module> 6 AttributeError: 'list' object has no attribute '__next__' 7 8 #用__iter__()生成迭代器,迭代器可以被__next__调用,每次返回一个值,直到没有数据时抛出StopIteration错误 9 >>> a = [1,2,3,4] 10 >>> b = a.__iter__() 11 >>> print(type(b)) 12 <class 'list_iterator'> 13 >>> b.__next__() 14 1 15 >>> b.__next__() 16 2 17 >>> b.__next__() 18 3 19 >>> b.__next__() 20 4 21 >>> b.__next__() 22 Traceback (most recent call last): 23 File "<stdin>", line 1, in <module> 24 StopIteration 25 >>>
2.生成器
2.1 什么是生成器?
- 生成器就是迭代器,可以理解为一种数据类型,这种类型自动实现了迭代器协议.(其他的数据类型需要调用自己内置的__iter__方法)。
- 优点:
- 效率高,相当于边生产边出售
- 节省内存空间,按需构建,不是一次性构建
- 保留函数运行的状态。不是从头运行,是从上一次的状态执行
2.2 生成器在python中的表现形式
- 生成器表达式:类似于列表推导,但是,生成器按需返回结果的一个对象,而不是一次构建一个结果列表
- 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行
示例:
1 #普通函数 2 >>> def test(): 3 ... return 1 4 ... return 2 5 ... return 3 6 ... 7 >>> g = test() 8 >>> print(g) #遇到一个return就停止 9 1 10 >>> print(g) #遇到一个return就停止 11 1 12 >>> 13 >>> print(g.__next__()) #没有__next__()方法 14 Traceback (most recent call last): 15 File "<stdin>", line 1, in <module> 16 AttributeError: 'int' object has no attribute '__next__' 17 18 19 #生成器函数 20 >>> def test(): 21 ... yield 1 22 ... yield 2 23 ... yield 3 #和return不同的是可以返回多次 24 ... 25 >>> g = test() 26 >>> 27 >>> print(g.__next__()) #生成器自动实现了迭成器,所以会有__next__()方法。 28 1 29 >>> print(g.__next__()) #第二次运行保存上一次运行的结果,就是上面说的保存状态。 30 2 31 >>> print(g.__next__()) 32 3 33 >>> print(g.__next__()) #和迭代器一样,取完了就报StopIteration错。 34 Traceback (most recent call last): 35 File "<stdin>", line 1, in <module> 36 StopIteration 37 >>> 38 39 生成器函数示例
1 #比如我们要生成一个100万条数据的列表
2 #我们会这样生成
3 li = []
4 for i in range(1000000):
5 li.append(i)
6 print(li) #这样有个缺点,占用内存较大
7 >>> li = (x for x in range(1000000)) #这就是生成器表达式,和上面唯一不同的是,取一个,给一个,不占用空间。
8 >>> li.__next__()
9 0
10 >>> li.__next__()
11 1
12 >>> li.__next__()
13 2
14 >>> li.__next__()
15 3
16 >>>
2.4 生成器send的用法***
- send主要是用于外部与生成器对象的交互,send的参数是指(上一次被挂起的yield语句的返回值)***
1 #*** yield 相当于return ,return是控制函数的返回值的 2 #x=yield的另外一个特性,接受send传过来的值,赋值给x 3 4 def test(): 5 print('one') 6 first = yield 1 7 print('two', first) 8 yield 2 9 print('three') 10 yield 3 11 res = test() 12 13 #第一次调用 14 print(res.__next__()) 15 #结果(本次结果one是正常打印,None相当于没有返回值) 16 one 17 None 18 19 #第二种调用send(本次结果one是正常打印,None是函数没有返回值,two 是正常打印,"我是first"发送给了first,也就是第一次返回的值,2是正常打印) 20 print(res.__next__()) 21 print(res.send('我是first')) # 这里的'我是first'就是上一次被挂起的yield语句的返回值。 22 #结果 23 one 24 None 25 two 我是first 26 2
2.5 生产者消费者模型
- 这里学习下生产者消费者模型,以简单介绍下生成器的好处及原理。巩固下send的知识。
首先假设,路边拔丝蛋糕的买卖(两种方法)
1.第一种实现方法,做好了100盒准备卖,用100秒做好100盒蛋糕,然后一盒一盒的卖,来买蛋糕的人只能等做好100盒才能买。所有人都等着。
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 import time 4 def shengchan(): 5 dangao = [] 6 for i in range(100): 7 time.sleep(1) 8 dangao.append('第%s盒蛋糕' %i) 9 return dangao 10 11 def mai(res): 12 for index,dg in enumerate(res): 13 time.sleep(1) 14 print('第%s个人,买个%s' %(index,dg)) 15 16 result = shengchan() 17 mai(result) 18 19 结果: 20 第0个人,买个第0盒蛋糕 21 第1个人,买个第1盒蛋糕 22 第2个人,买个第2盒蛋糕 23 第3个人,买个第3盒蛋糕 24 第4个人,买个第4盒蛋糕 25 第5个人,买个第5盒蛋糕 26 第6个人,买个第6盒蛋糕 27 第7个人,买个第7盒蛋糕 28 第8个人,买个第8盒蛋糕 29 第9个人,买个第9盒蛋糕 30 第10个人,买个第10盒蛋糕 31 .... 32 第99个人,买个第99盒蛋糕
2.第二种实现方法,做一盒卖一盒,用1秒做一盒蛋糕被人买走了,又做了一盒又被人买走了,所有买蛋糕的人只等一秒就开心的买走了想要的蛋糕。
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 import time 4 def mai(name): 5 print('我是[%s],我要买一盒拔丝蛋糕' %name) 6 while True: 7 dangao=yield 8 time.sleep(1) 9 print('%s 买走了[%s]一盒' %(name,dangao)) 10 11 12 def shengchan(): 13 c1 = mai('zhangsan') 14 c2 = mai('lisi') 15 c3 = mai('wangwu') 16 c4 = mai('zhaoliu') 17 c1.__next__() 18 c2.__next__() 19 c3.__next__() 20 c4.__next__() 21 for i in range(100): 22 time.sleep(1) 23 c1.send('拔丝蛋糕') 24 c2.send('拔丝蛋糕') 25 c3.send('拔丝蛋糕') 26 c4.send('拔丝蛋糕') 27 shengchan() 28 29 结果: 30 我是[zhangsan],我要买一盒拔丝蛋糕 31 我是[lisi],我要买一盒拔丝蛋糕 32 我是[wangwu],我要买一盒拔丝蛋糕 33 我是[zhaoliu],我要买一盒拔丝蛋糕 34 zhangsan 买走了拔丝[蛋糕]一盒 35 lisi 买走了拔丝[蛋糕]一盒 36 wangwu 买走了拔丝[蛋糕]一盒 37 zhaoliu 买走了拔丝[蛋糕]一盒 38 zhangsan 买走了拔丝[蛋糕]一盒 39 lisi 买走了拔丝[蛋糕]一盒 40 wangwu 买走了拔丝[蛋糕]一盒 41 zhaoliu 买走了拔丝[蛋糕]一盒
总结:第二种方法要比第一种方法的效率高,这就是生成器的好处。边做边买。
2.6 列表解析&三元表达式简介
2.6.1 三元表达式
1 #基本判断 2 >>> x=1 3 >>> y=2 4 >>> if x > y: 5 ... print(x) 6 ... else: 7 ... print(y) 8 ... 9 2 10 >>> 11 12 #三元表达式判断 13 >>> x=1 14 >>> y=2 15 >>> res= print(x) if x > y else print(y) 16 2 17 >>> 18 19 #函数返回 20 >>> def max_xy(x, y): 21 ... if x > y: 22 ... return x 23 ... else: 24 ... return y 25 ... 26 >>> max_xy(1,2) 27 2 28 >>> 29 #三元表达式返回 30 >>> def max_xy(x, y): 31 ... return x if x > y else y 32 ... 33 >>> max_xy(1,2) 34 2 35 >>>
2.6.2 列表解析
1 #普通方式 2 3 >>> a='hello' 4 >>> l=[] 5 >>> for i in a: 6 ... res=i.upper() 7 ... l.append(res) 8 ... 9 >>> print(l) 10 ['H', 'E', 'L', 'L', 'O'] 11 >>> 12 13 #列表解析方式 14 15 >>> a='hello' 16 >>> l=[i.upper() for i in a] 17 >>> print(l) 18 ['H', 'E', 'L', 'L', 'O'] 19 >>> 20 21 #普通方式 22 23 >>> l=[1,2,3,4,5] 24 >>> l_new=[] 25 >>> for i in l: 26 ... if i > 3: 27 ... l_new.append(i) 28 ... 29 >>> 30 >>> print(l_new) 31 [4, 5] 32 >>> 33 34 #列表解析方式 35 >>> l=[1,2,3,4,5] 36 >>> res=[i for i in l if i > 3] 37 >>> print(res) 38 [4, 5] 39 >>> 40 41 #普通方式 42 43 >>> l=[] 44 >>> for i in range(10): 45 ... l.append(i) 46 ... 47 >>> print(l) 48 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 49 >>> 50 51 #列表解析方式 52 53 >>> res=[i for i in range(1,10)] 54 >>> print(res) 55 [1, 2, 3, 4, 5, 6, 7, 8, 9] 56 >>> 57 58 #普通方式 59 60 >>> ret = [] 61 >>> for x in range(5): 62 ... if x % 2 == 0: 63 ... for y in range(5): 64 ... if y % 2 ==1: 65 ... ret.append((x,y)) 66 ... 67 >>> print(ret) 68 [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)] 69 print(ret) 70 71 #列表解析方式 72 >>> ret = [(x,y) for x in range(5) if x % 2==0 for y in range(5) if y % 2 ==1] 73 >>> print(ret) 74 [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)] 75 >>>
3.装饰器
3.1 什么是装饰器
- 借鉴了知乎上大神形象的比喻,内裤可以用来遮羞,但是到了冬天它没法为我们防风御寒,聪明的人们发明了长裤,有了长裤后宝宝再也不冷了,装饰器就像我们这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。^_^.....^_^
- 定义:装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
- 遵循以下两个原则:
1.不能修改被修饰函数的源代码
2.不能修改被修饰函数的调用方式
- 装饰器储备知识:装饰器=高阶函数+函数嵌套+闭包(缺一不可)
3.2 装饰器的好处
- 比如要实现计算一个函数的运行时间,示例。
原始函数
1 import time 2 def cale(n): 3 res = 0 4 for i in n: 5 time.sleep(1) 6 res+=i 7 return res 8 9 print(cale(range(10)))
计算函数的运行时间(普通方式)
1 import time 2 def cale(n): 3 start_time = time.time() 4 res = 0 5 for i in n: 6 time.sleep(1) 7 res+=i 8 stop_time = time.time() 9 print('函数的运行时间是%s' %(stop_time-start_time)) 10 return res 11 print(cale(range(10)))
这里我们可以实现,在函数之中加了个计算时间的逻辑,如果需求是要计算所有函数的运行时间,那么就显得笨重。这时候就用到装饰器了。
计算函数的运行时间(装饰器方式)
#装饰器 import time def zhuangshi(func): #高阶函数,参数是一个函数 def wapper(*args,**kwargs): #函数之内定义函数,是函数的嵌套 start_time = time.time() #闭包:在一个作用域里放入定义变量,相当于打了一个包 res = func(*args,**kwargs) stop_time = time.time() #闭包:在一个作用域里放入定义变量,相当于打了一个包 print('函数的运行时间是%s' % (stop_time - start_time)) return res return wapper #高阶函数,返回值是一个函数 @zhuangshi #@zhuangshi是装饰器的用法,要装饰哪个函数就在函数前加“@装饰器名(装饰器函数名)”等同于cale=zhuangshi(cale) def cale(n): res = 0 for i in n: time.sleep(1) res+=i return res print(cale(range(10))) #符合装饰器的原则,调用方式不变 #结论=======>装饰器=高阶函数+函数嵌套+闭包
这样就实现了在任何函数前面加@zhuangshi,就可以计算函数的运行时间了。
3.3 带参数的装饰器
- 无参数装饰器(以登录京东、访问家目录、访问购物车、为例的装饰器)
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 #用户列表 5 user_list = [ 6 {'name':'zhangsan','passwd':'123'}, 7 {'name':'lisi','passwd':'456'}, 8 {'name':'wangwu','passwd':'789'}, 9 ] 10 #全局变量一改,其他子程序都会改,用来跟踪用户的登录状态 11 user_dic={'username':None,'login':False} 12 13 #无参数装饰器 14 def auth_func(func): 15 def wapper(*args, **kwargs): 16 if user_dic['username'] and user_dic['login']: # 这里判断状态 17 res = func(*args, **kwargs) 18 return res 19 username = input('username: ').strip() 20 passwod = input('password: ').strip() 21 for userdic in user_list: 22 if username == userdic['name'] and passwod == userdic['passwd']: 23 user_dic['username'] = username 24 user_dic['login'] = True 25 res = func(*args, **kwargs) 26 return res 27 else: 28 print('用户名或密码错误') 29 return wapper 30 31 @auth_func 32 def index(): 33 print('欢迎来到京东主页') 34 35 @auth_func 36 def home(name): 37 print('欢迎%s来到家目录' %name) 38 39 @auth_func 40 def shopping_car(name): 41 print('欢迎来到购物车,%s的购物车里有[%s,%s,%s]' %(name,'洗面奶','牛奶','酸奶')) 42 43 print('开始---------------------->',user_dic) 44 index() 45 print('结束---------------------->',user_dic) 46 47 print('\n') 48 print('开始---------------------->',user_dic) 49 home('all is well') 50 print('结束---------------------->',user_dic) 51 print('\n') 52 53 print('开始---------------------->',user_dic) 54 shopping_car('all is well') 55 print('结束---------------------->',user_dic) 56 57 ################################################# 58 #输出结果 59 60 开始----------------------> {'login': False, 'username': None} 61 username: zhangsan 62 password: 123 63 欢迎来到京东主页 64 结束----------------------> {'login': True, 'username': 'zhangsan'} 65 66 67 开始----------------------> {'login': True, 'username': 'zhangsan'} 68 欢迎all is well来到家目录 69 结束----------------------> {'login': True, 'username': 'zhangsan'} 70 71 72 开始----------------------> {'login': True, 'username': 'zhangsan'} 73 欢迎来到购物车,all is well的购物车里有[洗面奶,牛奶,酸奶] 74 结束----------------------> {'login': True, 'username': 'zhangsan'}
- 带参数装饰器(给装饰器加上参数,参数为登录京东、访问家目录、访问购物车、加上不同的验证类型)
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 4 #用户列表 5 user_list = [ 6 {'name':'zhangsan','passwd':'123'}, 7 {'name':'lisi','passwd':'456'}, 8 {'name':'wangwu','passwd':'789'}, 9 ] 10 #全局变量一改,其他子程序都会改,用来跟踪用户的登录状态 11 user_dic={'username':None,'login':False} 12 13 #带参数装饰器 14 #要给装饰器传参数这里我们就多加一层auth来实现,最外面层传参数,最里层能收到。 15 def auth(auth_type='filedb'): 16 def auth_func(func): 17 def wapper(*args,**kwargs): 18 if auth_type == 'auth_1': 19 print("认证类型是", auth_type) 20 if user_dic['username'] and user_dic['login']: #这里判断状态,session 21 res = func(*args, **kwargs) 22 return res 23 username = input('username: ').strip() 24 passwod = input('password: ' ).strip() 25 for userdic in user_list: 26 if username == userdic['name'] and passwod == userdic['passwd']: 27 user_dic['username'] = username 28 user_dic['login'] = True 29 res = func(*args,**kwargs) 30 return res 31 else: 32 print('用户名或密码错误') 33 elif auth_type == 'auth_2': 34 print("认证类型是", auth_type) 35 res = func(*args, **kwargs) 36 return res 37 else: 38 print("不知道的类型") 39 res = func(*args, **kwargs) 40 return res 41 return wapper 42 return auth_func 43 44 45 @auth(auth_type='auth_1') #这里参数为auth_1 46 def index(): 47 print('欢迎来到京东主页') 48 49 @auth(auth_type='auth_2') #这里参数为auth_2 50 def home(name): 51 print('欢迎%s来到家目录' % name) 52 53 @auth(auth_type='auth_3') #这里参数为auth_3 54 def shopping_car(name): 55 print('欢迎来到购物车,%s的购物车里有[%s,%s,%s]' % (name, '洗面奶', '牛奶', '酸奶')) 56 57 print('开始---------------------->',user_dic) 58 index() 59 print('结束---------------------->',user_dic) 60 61 print('\n') 62 print('开始---------------------->',user_dic) 63 home('all is well') 64 print('结束---------------------->',user_dic) 65 print('\n') 66 67 ################################################# 68 #输出结果 69 70 开始----------------------> {'username': None, 'login': False} 71 认证类型是 auth_1 72 username: zhangsan 73 password: 123 74 欢迎来到京东主页 75 结束----------------------> {'username': 'zhangsan', 'login': True} 76 77 78 开始----------------------> {'username': 'zhangsan', 'login': True} 79 认证类型是 auth_2 80 欢迎all is well来到家目录 81 结束----------------------> {'username': 'zhangsan', 'login': True} 82 83 84 开始----------------------> {'username': 'zhangsan', 'login': True} 85 不知道的类型 86 欢迎来到购物车,all is well的购物车里有[洗面奶,牛奶,酸奶] 87 结束----------------------> {'username': 'zhangsan', 'login': True}

浙公网安备 33010602011771号