装饰器
1. 装饰器铺垫,装饰器的本质是闭包函数

1 # 装饰器 在python中占有举足轻重的位置 2 3 4 # 装饰器形成的过程 5 # 装饰器的作用 6 # 原则:开放封闭原则 7 # 开放 8 # 对扩展是开放的,新增功能是可以的 9 # 封闭 10 # 对修改是封闭的,就是发布后版本的版本的代码,就会封版,如python 的open,版本不同不能去改。 11 # 所以不能轻易的修改已经写好的代码,如果要修改,可以使用装饰器,装饰器就是解决这个的。 12 # 如果直接改原先的函数,那叫做重构 13 # 装饰器的固定模式 14 15 16 import time # 导入时间模块 17 #time.time() # 得到当前时间戳,从1970年1月1日到现在的秒数 18 #time.sleep(5) # 睡5秒 19 20 21 # 测试函数执行时间的例子, 22 # def timmer(f): 23 # starttime = time.time() 24 # f() 25 # endtime = time.time() 26 # print(endtime - starttime) 27 # 28 # def func(): 29 # #starttime = time.time() 30 # print('老板好同事好大家好') 31 # #endtime = time.time() 32 # #print(endtime - starttime) 33 # 34 # timmer(func) 35 36 # --------------------------------------# 37 def func(): 38 #starttime = time.time() 39 print('老板好同事好大家好') 40 #endtime = time.time() 41 #print(endtime - starttime) 42 43 def timmer(f): # timmer是装饰器函数,f就是被装饰的函数 44 def inner(): 45 starttime = time.time() 46 f() 47 endtime = time.time() 48 print(endtime - starttime) 49 return inner 50 51 func = timmer(func) # 得到inner闭包函数地址,同事timmer的形参f会始终保存func函数的地址 52 func() # 调用了闭包函数inner 53 54 # 上面程序的流程意思:为了测试已经写好的函数的执行时间,func()函数就是之前写好的函数,现在要测试这个func函数执行时间,但不能改动func, 55 # 所以定义了一个timmer函数,函数体中定义了一个inner闭包函数(因为使用了timmer的形参f) 56 # 函数体中调用了timmer的形参f,f()前后加上了记录开始时间与结束时间,并在后面打印 57 # 使用时,func = timer(func),timer先被调用,形参是func函数名,将闭包函数inner地址返回,赋值给了func,由于闭包的特性 58 # timmer的形参f内存值是刚才传进去的func地址,不会消失。所以在调用func时,是调用了 59 # inner闭包函数,闭包函数体重的f是func函数。 60 61 # 装饰器的作用:不想修改函数的调用方式,但是还想再原来的函数前后添加功能 62 # timmer就是一个装饰器函数,装饰了func函数,只是对一个函数 有一些装饰的作用
2.逐步走向完整的装饰器

# 语法糖的概念:形容一个代码被改造后,用起来很爽,很开心。 # 语法糖的例子,例如上面装饰器函数和被装饰的函数的例子,如果在被装饰的函数的上面,加一个(@装饰器函数名)的字样 # 就相当于默认使用了func = timmer(func) @timmer # 语法糖,有了这句话,下面的 func = timmer(func)就不调用了,默认就告诉func这个被装饰的函数使用的装饰器是timmer def func(): # 被装饰的函数 #starttime = time.time() print('老板好同事好大家好') #endtime = time.time() #print(endtime - starttime) def timmer(f): # timmer是装饰器函数,f就是被装饰的函数 def inner(): starttime = time.time() f() endtime = time.time() print(endtime - starttime) return inner #func = timmer(func) # 因为在被装饰的函数的上面加了(@装饰器函数名),所以这行代码可以不用,默认就具有了这行代码的效果 func() # 调用了闭包函数inner # -进一步:装饰器装饰函数,将被装饰的函数的返回值返回-# def timmer2(f): # timmer是装饰器函数,f就是被装饰的函数 def inner(): starttime = time.time() ret = f() # 运行被装饰的函数,并接收被装饰的函数的返回值 endtime = time.time() print(endtime - starttime) return ret # 将被装饰的函数的返回值返回 return inner @timmer2 def func2(): print('老板好同事好大家好') return '新年好' #func2 = timmer2(func2) ret = func2() # 调用了闭包函数inner print(ret) # 新年好 # -再进一步:装饰带参数和带返回值的函数。装饰器装饰函数,将被装饰的函数的参数传递进去,返回值也要返回,# def timmer3(f): # timmer是装饰器函数,f就是被装饰的函数 def inner(a, b): # 加入被装饰的函数需要的参数 starttime = time.time() ret = f(a, b) # 运行被装饰的函数,传递被装饰的函数需要的参数并接收被装饰的函数的返回值 endtime = time.time() print(endtime - starttime) return ret # 将被装饰的函数的返回值返回 return inner @timmer3 # 相当于执行了func3 = timmer3(func3) def func3(a, b): print('老板好同事好大家好', a, b) return '新年好' #func3 = timmer3(func3) ret = func3(1234567, 5) # 调用了闭包函数inner print(ret) # 新年好 # -再进一步:装饰带参数和带返回值的函数。装饰器装饰带N个参数的函数,被装饰器的函数有的是1个参数的,有的是2个参数,参数是不知道有多少个的。这是完整的装饰器应有的东西 def timmer4(f): # timmer是装饰器函数,f就是被装饰的函数 def inner(*args, **kwargs): # 加入被装饰的函数需要的参数,使用*args接收动态的位置参数,使用**kwargs接收动态的关键字参数。此种方法接收参数为万能 starttime = time.time() ret = f(*args, **kwargs) # 运行被装饰的函数,传递被装饰的函数需要的参数并接收被装饰的函数的返回值,调用函数时,传递的参数加上*表示打散后传入,打散成元组 endtime = time.time() print(endtime - starttime) return ret # 将被装饰的函数的返回值返回 return inner @timmer4 # 相当于执行了func3 = timmer3(func3) def func4(a): print('老板好同事好大家好', a) return '新年好' @timmer4 # 相当于执行了func3 = timmer3(func3) def func5(a, b): print('老板好同事好大家好', a, b) return '新年好' #func3 = timmer3(func3) ret = func5(1234567, 5) # 调用了闭包函数inner ret = func4(1234567) # 调用了闭包函数inner print(ret) # 新年好 # ----------------------------------- # 装饰器的定义的固定模式 def wrapper(f): # wrapper是装饰器函数名字,f是被装饰的函数 def inner(*args, **kwargs): # 加入被装饰的函数需要的参数,使用*args接收动态的位置参数,使用**kwargs接收动态的关键字参数。此种方法接收参数为万能 '''在被装饰函数的之前要做的事''' ret = f(*args, **kwargs) # 运行被装饰的函数,传递被装饰的函数需要的参数并接收被装饰的函数的返回值,调用函数时,传递的参数加上*表示打散后传入,打散成元组 '''在被装饰函数的之后要做的事''' return ret # 将被装饰的函数的返回值返回 return inner # 返回inner()函数地址 @wrapper # 相当于执行了func6 = wrapper(func6) # 有了这句话,被装饰的函数实际调用的时候,调用的是装饰器中inner的闭包函数 def func6(a): print('老板好同事好大家好', a) return '新年好'
3.装饰器的进阶

1 # 装饰器的进阶 2 # functools.wraps 3 # 带参数的装饰器 4 # 多个装饰器装饰同一个函数 5 6 7 8 9 # 补充 10 # def wahaha(): 11 # ''' 12 # 一个打印娃哈哈的函数 13 # :return: 14 # ''' 15 # print('娃哈哈') 16 # 17 # print(wahaha.__name__) # __name__查看字符串格式的函数名字 18 # print(wahaha.__doc__) # 查看函数的注释 19 20 from functools import wraps # wraps是一个装饰器模块 21 22 def wrapper(func_arg): # func_args == holiday 23 @wraps(func_arg) # 使用wraps装饰了inner函数,用这个装饰之后,出现的现象是holiday.__name__就不是inner了而是holiday 24 def inner(*args, **kwargs): 25 print('在被装饰函数之前做的事') 26 ret = func_arg(*args, **kwargs) 27 print('在被装饰函数之后做的事') 28 return ret 29 return inner 30 31 @wrapper # holiday = wrapper(holiday) 32 def holiday(day_arg): 33 print('全体放假{day}天'.format(day = day_arg)) 34 return '真的' 35 36 print(holiday(5)) 37 print(holiday.__name__) # inner 当装饰器函数中的闭包函数inner没有被wraps装饰时,这里打印的是inner的名字,当wraps装饰器装饰了装饰器中的闭包函数inner时,这里打印holiday 38 39 40 41 42 # 如果有500个函数用了一个装饰器,现在不想用了,一一个去除太麻烦,可以使用带参数的装饰器 43 # 带参数的装饰器,就是三层的装饰器 44 import time 45 46 FLAG = True 47 48 def timmer_out(flag): 49 def timmer(func): 50 def inner(*args, **kwargs): 51 if flag: 52 start = time.time() 53 ret = func(*args, **kwargs) 54 end = time.time() 55 print(end - start) 56 else: 57 ret = func(*args, **kwargs) 58 return ret 59 return inner 60 return timmer 61 62 #timer = timer_out(FLAG) 63 @timmer_out(FLAG) # 因为有(),所以调用timmer_out函数,参数是FLAG,因为timer_out将参数FLAG传了进去,FLAG为True,所以FLAGH为Ture的值被保留,timmer_out函数返回了timmer的地址,所以变成了@timmer,timer = timer(wahaha) 64 def wahaha(): 65 time.sleep(0.1) 66 print('haha') 67 68 @timmer_out(FLAG) 69 def erguotou(): 70 time.sleep(0.1) 71 print('erguotou') 72 73 wahaha() 74 erguotou() 75 76 77 # 多个装饰器装饰一个函数, 78 # 深入理解 按照程序运行过程一点一点分析理解。首先碰见了@wrapper1,需要装饰一个本装饰的函数,但是发现下面的@wrapper2不是函数,所以执行到@wrapper2,@wrapper下面有一个函数 79 # 所以知道要装饰下面的函数f,所以@wrapper2自动变成了 f = wrapper2(f),wrapper2(f)执行完毕后,返回的是inner2,因为inner2是一个函数,所以此时@wrapper1发现下面的@wrapper2变成了一个函数inner2了, 80 # 所以@wrapper1又装饰了inner2,此时@wrapper1自动变成了 inner2 = wrapper1(inner2),wrapper1(inner2)执行完毕后,返回的是inner1,之后用调用了f(),所以先执行了inner1函数体,打印了'wrapper1, beform func' 81 # 之后到了func,因为inner1中的func是inner2的,所以执行了inner2函数体中的'wrapper2, beform func',之后执行到inner2中的func,因为inner2中的func是f,所以执行了f的函数体'in f',之后inner2中的func 82 # 执行完毕,执行到了'wrapper2. after func',之后inner2的函数体执行完毕,也就代表着inner1中的func执行完毕,所以执行到了inner1中的'wrapper1. after func',之后整体执行完毕 83 84 85 # 根据代码执行顺序也可更好理解:@wrapper2先被执行,此时f = wrapper2(f),执行结束后为 f = inner2,此时wrapper2中的func实际是f 86 # 之后@wrapper1被执行,此时f已经等于了inner2,所以inner2 = wrapper1(inner2),执行结束后返回了inner1 所以inner2变成了 inner1,同时wrapper1中的func实际是inner2 87 # 之后用户调用了f,所以先执行最后得到的返回值inner1的函数体,打印'wrapper1, beform func',之后运行了inner1中的func,因为inner1中的func是inner2,所以执行了inner2的函数体打印了wrapper2, beform func' 88 # 之后执行到了inner2中的func,因为inner2中的func是f,所以执行了f的函数体打印了'in f',之后f退出,执行到了inner2中的'wrapper2. after func',之后inner返回了f的返回值 89 # 也就是inner1中的func函数被执行完毕,接收到了inner2结束后的返回值,之后执行到了inner1中的wrapper1. after func',之后inner1返回了,并将返回值返回 90 91 # 简单理解:先运行wrapper1装饰器装饰前的代码,再运行wrapper2装饰器前的代码,再运行f的代码,再顺运行wrapper2装饰后的代码,再运行wrapper1装饰后的代码 92 def wrapper1(func): # func -> inner2 93 def inner1(*args, **kwargs): 94 print('wrapper1, beform func') 95 ret = func(*args, **kwargs) 96 print('wrapper1. after func') 97 return ret 98 return inner1 99 100 def wrapper2(func): # func -> f 101 def inner2(*args, **kwargs): 102 print('wrapper2, beform func') 103 ret = func(*args, **kwargs) 104 print('wrapper2. after func') 105 return ret 106 return inner2 107 108 @wrapper1 # f = wrapper1(f) f = inner1 109 @wrapper2 # f = wrapper2(f) 执行结束后为 f = inner2 110 def f(): 111 print('in f') 112 113 114 f() 115 116 # wrapper1, beform func 117 # wrapper2, beform func 118 # in f 119 # wrapper2. after func 120 # wrapper1. after func 121 122 123 # 多个装饰器装饰一个函数的应用,记录用户的登录情况,计算这个登录函数的执行时间,则先记录登录情况,在计算函数的执行时间