我每一次接触到一个新概念的时候,脑袋中立马浮现的是:为什么要用这个东西?现有的东西不能代替吗?带着这样的疑问,开始装饰器的学习吧。
为什么要用这个东西?
为了在不动函数调用接口(也就是你开始的时候是怎么调用这个函数的就怎么调用)的同时给函数附加上一些功能。
#STEP1:一个简单的函数
import time
def summ():
time.sleep(2)
STEP1:增加功能
现在我想给函数增加一个功能:计算函数执行时间
import time
def summ():
time.sleep(2)
start_time=time.time()#time.time()是一个时间戳,计算从1970年到现在总共有多少秒
summ()
end_time=time.time()#执行完summ()后的时间戳
print(end_time-start_time)#两者相减,就是运算耗费时间
>>2.0003156661987305
STEP2:功能的简化:使用无参函数
我们在外部共增加了3行函数来对时间进行运算,这样是最开始的做法,但是有一个很麻烦的问题就是,我们每验算一个函数的运算时间,就要把这三行代码复制过去,太恶心,有没有什么办法呢,有,把这三行做成一个函数来进行调用,代码如下:
import time
def summ(a,b):
time.sleep(2)
def calculate_time():
start_time = time.time()
summ()
end_time=time.time()
print(end_time-start_time)
calculate_time()
>>2.000300168991089
STEP3:函数的复用:把整体当作一个参数传入
通过把这个计时步骤写成一个函数,我们只需要调用就可以了,再进一步,如果我其他的函数也想调用呢?很简单,把summ()当成一个参数传递进去就好了。代码完善后如下:
import time
def summ():
time.sleep(2)
def calculate_time(f):
start_time = time.time()
f()
end_time=time.time()
print(end_time-start_time)
calculate_time(summ)
>>2.0006697177886963
STEP4:为什么要用装饰器
这么做,我虽然能够很便捷地查询函数运行时间,但还是改变了调用接口。我想要的是我输入summ()的时候,自动打印出来一个运行时间,而不是我要专门去输入一个calculate_time(summ()),总之,还不够简洁,就像我们玩游戏,本来我们用W|A|S|D四个方向键用得好好的,你内部写了个程序,说,为了使得每次移动的时候,玩家知道自己移动的位置坐标,我们给四个方向指令增加了一个功能,这个功能可以让玩家在游戏中看到自己的坐标数字,但是,大家在按住WASD键盘的时候还要按住一个L(location)键盘激发这个功能,你说,大家会用你这个功能吗?老板娘会骂人吗?大家想要的功能是,我按住WASD任意一个键的时候,位置坐标你就给我显示出来得了,别整这么麻烦。
于是装饰器横空出世,目的就是为了,不改变指令的情况下增加一些功能。就像装修的时候我们不打掉一个承重墙,但是贴了一些装饰性的壁纸。
STEP5:装饰器的前身
import time
def summ():
time.sleep(2)
def calculate_time(f):
def inner():
start_time = time.time()
f()
end_time=time.time()
print(end_time-start_time)
return inner
summ=calculate_time(summ)
summ()
>>2.000295639038086
STEP6:装饰器出来了
这时候,我们只从calculate_time()把summ作为f传进去,并返回一个inner的值(inner会实现打印时间的功能),最后把这个值赋值给summ.
但每次都要summ=calclulate(summ)太麻烦,何况这个summ函数也会换成其他函数,于是我们用一个语法糖来代替这个写法,放在装饰器函数(此时的装饰器就是calculate_time这个函数)的下面。成果如下:
import time
def calculate_time(f):
def inner():
start_time = time.time()
f()
end_time=time.time()
print(end_time-start_time)
return inner
@calculate_time#语法糖,等价于上面的summ=calculate_time(summ)
def summ():#此时的这个函数也可以更换为其他函数。
time.sleep(2)
summ()
>>2.000237226486206
上面的是无参数调用,且没有返回值,下面给出一个有参数和返回值的案例
import time
def calculate_time(f):
def inner(*args,**kwargs):#这里添加两个参数
start_time = time.time()
ret=f(*args,**kwargs)#同样加载上面的参数
end_time=time.time()
print(end_time-start_time)
return ret#设置一个返回值,这个返回值就是要装饰函数的返回值,本例是add(),返回a+b
return inner
@calculate_time
def add(a,b):
time.sleep(2)
return a+b
print(add(3,4))#把时间和函数返回值打印出来
>>2.000807523727417
>>7