七.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__方法)。
  • 优点:
  1. 效率高,相当于边生产边出售
  2. 节省内存空间,按需构建,不是一次性构建
  3. 保留函数运行的状态。不是从头运行,是从上一次的状态执行

2.2 生成器在python中的表现形式

  1. 生成器表达式:类似于列表推导,但是,生成器按需返回结果的一个对象,而不是一次构建一个结果列表
  2. 生成器函数:常规函数定义,但是,使用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
send示例

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盒蛋糕
买蛋糕方法1

  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

总结:第二种方法要比第一种方法的效率高,这就是生成器的好处。边做边买。

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}
带参数装饰器示例

 

 

  

posted @ 2017-08-07 18:06  Mr.gaofubin  阅读(169)  评论(0)    收藏  举报