一、装饰器的概念
器即函数
装饰即修饰
定义:本质就是函数,功能是为其他函数添加新功能。
二、装饰器需要遵循的原则
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()