一、装饰器的概念

器即函数

装饰即修饰

定义:本质就是函数,功能是为其他函数添加新功能。

二、装饰器需要遵循的原则

1、不修改被装饰函数的源代码(开放封闭原则)。

2、为被装饰函数添加新功能后,不修改被修饰函数的调用方式。

三、实现装饰器的必备条件

装饰器=高阶函数+函数嵌套+闭包(包是一套嵌套一套,闭包是封装变量)

四、高阶函数

高阶函数定义:

1、函数接收的参数是一个函数名。

2、函数返回值是一个函数名。

满足上述任意一个条件,即为高阶函数。

 1 #高阶函数
 2 # def foo():
 3 #     print('我的函数名作为参数传给高阶函数')
 4 # def soo(fun):  #传入参数
 5 #     print('我是高阶函数,我接受的参数名%s'%fun)
 6 #     fun()
 7 #
 8 # soo(foo)
 9 #
10 # def soo1(fun):
11 #     print('我是高阶函数2,我的返回值%s'%fun)
12 #     return fun   #返回参数
13 # soo1(foo)
高阶函数
 1 import time
 2 def foo():
 3     time.sleep(3)
 4     print('from foo')
 5 def timmer(fun):
 6     start_time=time.time()
 7     fun()
 8     end_time=time.time()
 9     print('函数运行时间%s'%(end_time-start_time))
10 timmer(foo)
11 # 运行结果:from foo
12 #          函数运行时间3.0
把函数当做参数传给高阶函数

小结:确实为foo函数增加了运行时间的功能,但foo函数原来执行它是foo(),现在实现这个功能执行是timmer(foo),改变了函数的调用方式。

 1 import time
 2 def foo():
 3     time.sleep(3)
 4     print('来自foo')
 5 def timmer(fun):
 6     start_time=time.time()
 7     return fun
 8     end_time=time.time()
 9     print('函数运行时间%s'%(end_time-start_time))
10 
11 foo=timmer(foo)
12 foo()
函数的返回值是函数名

小结:确实没有改变foo的调用方法,但是也没有为foo函数添加新功能。

两者都没满足装饰的遵循原则

五、嵌套函数

一个函数嵌套一个函数

1 #遵循作用域原则  全局变量 局部变量(变量一般先从局部找,找不到一级级往上找直至找到)遵循风湿理论(运行顺序)
2 def father(name):
3     print('from father %s'%name)
4     def son():
5         print('from son')
6         def grandson():
7             print('from grandson')
8         son()
9 father('hello-li')

六、闭包

 1 #闭包   包一层套一层  闭包封闭的包  封装变量  风湿理论
 2 def father(name):
 3     def son():
 4         name='listen'
 5         print('我的爸爸是%s'%name)
 6         def grandson():
 7             name='nihao'
 8             print('我的也也是%s'%name)
 9         grandson()
10     son()
11 father('hello-li')

七、无参数装饰器

无参数装饰器=高阶函数+函数嵌套

基本框架

1 #装饰器的基本框架
2 def timer(func):
3       def wrapper():
4           func()
5       return wrapper

加上参数

1  def timer(func):
2        def wrapper(*args,**kwargs):
3              func(*args,**kwargs)
4        return wrapper

 

 1 # _*_ encoding:utf-8 _*_
 2 __author__ = 'listen'
 3 __date__ = '2018/11/22 20:46'
 4 import time
 5 # def timer(fun):
 6 # #     def wrapper(name,age,):
 7 # #         start_time=time.time()
 8 # #         ret=fun(name,age)  #wrapper的参数和fun参数保持一样,作用域会找上一级的参数,必须保持一致
 9 # #         stop_time=time.time()
10 # #         print('这是函数的运行时间:%s'%(stop_time-start_time))
11 # #         return ret   #返回函数test的返回值
12 # #
13 # #     return wrapper
14 #change
15 def timer(fun):
16     def wrapper(*args,**kwargs):  #参数长度可以为任意的  列表或元组()     dict{ }
17         start_time=time.time()
18         ret=fun(*args,**kwargs)  #wrapper的参数和fun参数保持一样,作用域会找上一级的参数,必须保持一致
19         stop_time=time.time()
20         print('这是函数的运行时间:%s'%(stop_time-start_time))
21         return ret   #返回函数test的返回值
22 
23     return wrapper
24 
25 def test(name,age,gender):
26     time.sleep(3)
27     print('函数运行完毕,名字是%s 年龄是%s 性别是%s'%(name,age,gender))
28     return '你好'   #返回被调用函数的返回值  ‘你好’
29 test=timer(test)   #这个变量叫什么都行,为了不改变函数的调用方式所以起了和被调用函数名一样的名字
30 print(test('alex',18,'male'))  #运行不会把返回值打印出来,打印才会把返回值打印出来
31 
32 @timer
33 def test1(name,age):
34     time.sleep(1)
35     print('函数执行完毕,名字是%s,年龄是%s'%(name,age))
36     return 'hi'
37 test1('listen',20)
38 #这样导致不能同时使用语法糖,报参数错误,所以会因为参数的个数、名字导致得修改装饰器,所以装饰器可以修改为:
39 #运行结果:
40 # 函数运行完毕,名字是alex 年龄是18 性别是male
41 # 这是函数的运行时间:3.000345468521118
42 # 你好
43 # 函数执行完毕,名字是listen,年龄是20
44 # 这是函数的运行时间:1.0004689693450928
加上参数

 

加上功能

1 import time
2 def timer(func):
3     def wrapper(*args,**kwargs):
4         start_time=time.time()
5         func(*args,**kwargs)
6         end_time=time.time()
7         print('函数%s,运行时间是%s'%(func,end_time-start_time))
8     return wrapper

加上返回值

1 import time
2 def timer(func):
3     def wrapper(*args,**kwargs):
4         start_time=time.time()
5         res=func(*args,**kwargs)
6         end_time=time.time()
7         print('函数%s,运行时间是%s'%(func,end_time-start_time))
8         return res
9     return wrapper

 

 1 # _*_ encoding:utf-8 _*_
 2 __author__ = 'listen'
 3 __date__ = '2018/11/22 20:28'
 4 import time
 5 def timer(fun):
 6     def wrapper():
 7         start_time=time.time()
 8         ret=fun()
 9         stop_time=time.time()
10         print('这是函数的运行时间:%s'%(stop_time-start_time))
11         return ret   #返回函数test的返回值
12 
13     return wrapper
14 
15 def test():
16     time.sleep(3)
17     print('函数运行完毕')
18     return '你好'   #返回被调用函数的返回值  ‘你好’
19 test=timer(test)   #这个变量叫什么都行,为了不改变函数的调用方式所以起了和被调用函数名一样的名字
20 print(test())  #运行不会把返回值打印出来,打印才会把返回值打印出来
21 #结果:
22 # 函数运行完毕
23 # 这是函数的运行时间:3.0002875328063965
24 # 你好
加上返回值

 

使用装饰器

 

 1 import time
 2 def timer(func):
 3     def wrapper(*args,**kwargs):
 4         start_time=time.time()
 5         res=func(*args,**kwargs)
 6         end_time=time.time()
 7         print('函数%s,运行时间是%s'%(func,end_time-start_time))
 8         return res
 9     return wrapper
10 
11 def cal(array):
12     res=0
13     for i in array:
14         res+=i
15     return res
16 cal=timer(cal)
17 cal(range(10))
完整版

 

1 def cal(array):
2     res=0
3     for i in array:
4         res+=i
5     return res
6 cal=timer(cal)
7 cal(range(10))

语法糖@

1 @timer   #timer相当于cal=timer(cal)
2 def cal(array):
3     res=0
4     for i in array:
5         res+=i
6     return res
7 cal=timer(cal)
8 cal(range(10))

 

 1 # _*_ encoding:utf-8 _*_
 2 __author__ = 'listen'''
 3 __date__ = '2018/11/23 22:11'
 4 user_list=[
 5     {'username':'alex','passwd':'123'},
 6     {'username':'listen','passwd':'456'},
 7     {'username':'peiqi','passwd':'789'}
 8 ]
 9 current_user={'username':None,'login':False}
10 def auth_fun(fun):
11     def wrapper(*args,**kwargs):
12         if current_user['username'] and current_user['login']:
13             res = fun(*args, **kwargs)
14             return res
15         username=input('请输入用户名: ').strip()
16         passwrd=input('请输入密码: ').strip()
17         for k in user_list:
18             if username==k['username'] and passwrd==k['passwd']:
19                 current_user['username']=username
20                 current_user['login']=True
21                 res=fun(*args,**kwargs)
22                 return res
23         else:
24             print('用户名或密码输入错误!')
25     return wrapper
26 
27 
28 
29 @auth_fun
30 def home():
31     print('欢迎来到京东主页')
32     return 'welcome'
33 
34 @auth_fun
35 def shopping():
36     print( '我的购物车里面有好多好吃的')
37 
38 
39 def like():
40     return '我喜欢逛衣服'
41 # home()
42 print(current_user)
43 home()
44 print(current_user)
45 shopping()
无参实现认证功能

八、带参数的装饰器

 1 # _*_ encoding:utf-8 _*_
 2 __author__ = 'listen'''
 3 __date__ = '2018/11/23 22:11'
 4 user_list=[
 5     {'username':'alex','passwd':'123'},
 6     {'username':'listen','passwd':'456'},
 7     {'username':'peiqi','passwd':'789'}
 8 ]
 9 current_user={'username':None,'login':False}
10 def auth(auth_type='filedb'):  #为了让每一层都能用到参数的变量只能在最外一层加上,加了一个函数带参数的,返回的还是原来装饰器的函数名,这样可以保证不改变原来的运行顺序
11     def auth_fun(fun):
12         def wrapper(*args,**kwargs):
13             if auth_type == 'filedb':
14                 print('认证类型是:',auth_type)
15                 if current_user['username'] and current_user['login']:
16                     res = fun(*args, **kwargs)
17                     return res
18                 username=input('请输入用户名: ').strip()
19                 passwrd=input('请输入密码: ').strip()
20                 for k in user_list:
21                     if username==k['username'] and passwrd==k['passwd']:
22                         current_user['username']=username
23                         current_user['login']=True
24                         res=fun(*args,**kwargs)
25                         return res
26                 else:
27                     print('用户名或密码输入错误!')
28             elif auth_type == 'mysql':
29                 print('认证类型是:',auth_type)
30                 res = fun(*args, **kwargs)
31                 return res
32             else:
33                 print('不知道的认证类型')
34         return wrapper
35     return auth_fun
36 
37 
38 
39 @auth(auth_type='filedb')
40 def home():
41     print('欢迎来到京东主页')
42     return 'welcome'
43 
44 @auth(auth_type='mysql')
45 def shopping():
46     print( '我的购物车里面有好多好吃的')
47 
48 @auth(auth_type='hello')
49 def like():
50     return '我喜欢逛衣服'
51 # home()
52 # print(current_user)
53 home()
54 # print(current_user)
55 shopping()
56 like()

 模拟存放在数据库中:

 1 # _*_ encoding:utf-8 _*_
 2 __author__ = 'listen'''
 3 __date__ = '2018/11/23 22:11'
 4 
 5 current_user={'username':None,'login':False}
 6 def auth(auth_type='filedb'):  #为了让每一层都能用到参数的变量只能在最外一层加上,加了一个函数带参数的,返回的还是原来装饰器的函数名,这样可以保证不改变原来的运行顺序
 7     def auth_fun(fun):
 8         def wrapper(*args,**kwargs):
 9             if auth_type == 'filedb':
10                 print('认证类型是:',auth_type)
11                 if current_user['username'] and current_user['login']:
12                     res = fun(*args, **kwargs)
13                     return res
14                 username=input('请输入用户名: ').strip()
15                 passwrd=input('请输入密码: ').strip()
16                 f=open('filedb','r',encoding='utf-8')
17                 for k in f:
18                     if username==eval(k)['username'] and passwrd==eval(k)['passwd']:  #用eval()函数把文件类型变为原来的数据类型
19                         current_user['username']=username
20                         current_user['login']=True
21                         res=fun(*args,**kwargs)
22                         return res
23                 else:
24                     print('用户名或密码输入错误!')
25             elif auth_type == 'mysql':
26                 print('认证类型是:',auth_type)
27                 res = fun(*args, **kwargs)
28                 return res
29             else:
30                 print('不知道的认证类型')
31         return wrapper
32     return auth_fun
33 
34 
35 
36 @auth(auth_type='filedb')
37 def home():
38     print('欢迎来到京东主页')
39     return 'welcome'
40 
41 @auth(auth_type='mysql')
42 def shopping():
43     print( '我的购物车里面有好多好吃的')
44 
45 @auth(auth_type='hello')
46 def like():
47     return '我喜欢逛衣服'
48 # home()
49 # print(current_user)
50 home()
51 # print(current_user)
52 shopping()
53 like()
View Code