装饰器
装饰器形成的过程:最简单的装饰器——有返回值的——有一个参数——万能参数
装饰器的作用:不想修改函数的调用方式 但是还想在原来的函数前后添加功能
原则:开放封闭原则
语法糖:@装饰器函数名
装饰器的固定模式
原则:开放封闭原则
开放:对扩展是开放的
封闭:对修改是封闭的
首先我们来逐步实现这么一个功能吧
计算代码运行的时间
#先来看下需要的组件 import time #time 模块 time.time() #获取当前时间 time.sleep(5) #让程序睡眠多少时间 print(time.time()) #来试下
我们来写一个可以计算时间的函数
def func(): start=time.time() time.sleep(0.1) #时间太短 系统会显示 0.0,所以我们让他休眠 0.1s print("计算程序执行的时间") end=time.time() print(end-start) func()
但是这里有一个问题:如果要计算很多函数的运行时间,那不是要在函数中都加上几行计算的代码,这显然是不可行的,一般写好的没问题的函数,是不会去对它进行修改,而且还很麻烦
所以这里我们写一个计算时间的函数独立出来
def times(f): start=time.time() time.sleep(0.1) f() end=time.time() print(end-start) def func(): print("计算程序执行的时间") def func2(): print(time.sleep(5)) times(func) times(func2)
但是这样貌似还有一个问题,我要计算时间的函数都要调用 times() 函数,这样其实是很麻烦的
最终还是要调用 func() 就可以计算运行时间,就是说你调用的是 func() 其实调用的 times() 这就很完美了
接着我们来了解下装饰器吧,解决这样的问题
先来写一个简单的装饰器
#一个简单的装饰器 import time def func(): time.sleep(0.1) print('你好,世界') def timmer(f): #装饰器函数 def inner(): start = time.time() f() #被装饰的函数 end = time.time() print(end - start) return inner func = timmer(func) #通过这一步就可以使用 func 调用 timmer 了 func()
我们这里做的事情就是:在不想修改函数的调用方式的情况下 但还想在原来的函数前后添加功能
接着我们来看另一个甜甜的东西——语法糖
import time def timmer(f): #装饰器函数 def inner(): start = time.time()
time.sleep(0.1) f() #被装饰的函数 end = time.time() print(end - start) return inner @timmer #语法糖 替代下面 func = timmer(func) 这句话 def func(): time.sleep(0.1) print('你好,世界') #func = timmer(func) #通过这一步就可以使用 func 调用 timmer 了 func()
然后我们来看接收返回值的装饰器
import time def timmer(f): #装饰器函数 def inner(): start=time.time() time.sleep(0.1) ret = f() #被装饰的函数 end=time.time() print(end-start) return ret #接收 func() 函数的返回值 return inner #这个地方要穿函数的名字 而不是加括号 @timmer #语法糖 替代下面 func = timmer(func) 这句话 def func(): print('你好,世界') return 'Hello' print(func()) #返回值打印 返回值为:Hello
最后我们来看带参数的装饰器,一个参数与万能参数是一样的,只是参数的不同罢了
#一个参数的装饰器 import time def timmer(f): #装饰器函数 def inner(a): #3.给 inner() 加上参数 start=time.time() time.sleep(0.1) ret = f(a) #4.由于 inner() 把参数传给了 f(),所以 f() 也要加上参数,最后测试传值 end=time.time() print(end-start) return ret #接收 func() 函数的返回值 return inner @timmer #语法糖 替代下面 func = timmer(func) 这句话 def func(a): #2.这里也要加上参数,由于执行 func 时,实际执行的 inner(),所以 inner() 也要加上参数 print("计算程序执行的时间",a) return 'Hello' print(func("123")) #1.加入参数 #输出结果 ''' 计算程序执行的时间 123 0.10937380790710449 Hello '''
###万能参数的装饰器 #可以看出 只是在 a 的位置更换了 *args **kwargs 来接收参数 import time def timmer(f): def inner(*args,**kwargs): start=time.time() time.sleep(0.1) ret = f(*args,**kwargs) end=time.time() print(end-start) return ret return inner @timmer def func(*args,**kwargs): print("计算程序执行的时间",args,kwargs) #2. 就是这个位置的 kwargs,如果写了,打印时会出现一个 ' {} ' 空字典,并不会报错 return 'Hello' print(func('1','2','3',k=100)) #1. 不需要关键字传参时,kwargs 可以不写 #输出结果 ''' 计算程序执行的时间 ('1', '2', '3') {'k': 100} 0.1093745231628418 Hello '''
好了,以上就是我们对于装饰器完善的最终结果了
装饰器的固定模式
def wrapper(f): #装饰器函数,f是被装饰的函数 def inner(*args,**kwargs): #定义一个内置函数,且和下面统级别的返回值名字一致且不加括号 #动态参数一定要原封不动的传给 被装饰的函数 f() '''在被装饰函数之前要做的事''' ret = f(*args,**kwargs) #被装饰的函数 函数执行完的返回值要原封不动传给 return '''在被装饰函数之后要做的事''' return ret return inner @wrapper #语法糖 @装饰器函数名 等价于 func = wrapper(func) def func(a,b): #被装饰的函数 time.sleep(0.01) print('你好:世界',a,b) return '新年好' #print(func(23,45)) #接收返回值 ret = func(23,45) #接收返回值 print(ret)