一.装饰器的作用
装饰器的作用就是在不改变原有函数调用方式和代码的情况下,给这个函数增加需要的功能。
例如:
def print_1(): print(1)
然后这个函数在项目的很多地方都被调用了,现在需要在不改变调用方式和函数代码的情况下,在调用print_1时先打印一下当前时间,这种时候就需要用到装饰器了。
二.装饰器的实现原理。
1.函数作为参数传给另一个函数
def print_1(): print (1) def print_time(func): print(time.time()) func() print_time(print_1) #将print_1作为参数传给print_time
执行结果:
1575794937.7757223
1
这里会先执行print(time.time()),再执行func(),因为传了print_1进来,这里func()就相当于print_1(),跟我们看到的结果一样
2.函数的嵌套
python允许在函数里定义另一个函数
def print_1(): print (1) def print_time(func): def inner(): print(time.time()) inner() func() print_time(print_1)
这里先执行的inner里的print(time.time),再执行func()
3.将函数作为返回值
def print_1(): print (1) def print_time(func): def inner(): print(time.time()) func() return inner tmp=print_time(print_1) #这里因为print_time反回inner函数,所以这一步相当于把inner函数赋值给tmp tmp() #然后这里tmp(),这里相当于调用了inner函数,然后就执行print(time.time())和func()
这个时候用tmp(),就完成了最初的目标每次调用print _1时都打印一下时间的效果了,但是还有个条件没满足,那就是不改变原有的调用方式,这里变成了tmp就不行了。
接下来再进一步:
print_1 = print_time(print_1) #这里把iprint_time返回值inner函数赋值给print_1 print_1()
这样就完全实现了最初的目标了,不改变函数调用方式也不修改函数代码,给print_1增加了功能。
然后,print_1 = print_time(print_1),这样的代码实在有点难看,所以python为我们提供了语法糖神奇的@符号。
只需要在print_1函数定义的时候加上@print_time即可,如:
@print_time #这一句相当于print_1 = print_time(print_1) def print_1(): print(1)
所以,完整的装饰器实现过程如下:
import time from functools import wraps #定义一个装饰器 def wrapper_print_time(func): @wraps(func) def inner(): print(time.time()) func() return inner #在需要用到装饰器的函数定义前加@装饰器名 @wrapper_print_time #相当于print_1 = wrapper_print_time(print_1) def print_1(): print (1) print_1()
@wraps(func)
这一句不加一样能实现装饰器的效果,加这一句是为了避免一些错误, 作用是将print_1函数的一些属性赋给inner函数
print(print_1.__name__)
装饰器里不加@wraps(func)的话,这里会打印出inner,如果加了的话则会打印出print_1
三.可以接收参数的装饰器
现在多一个print_2函数,有个需求就是print_1和print_2函数调用时都要先打印时间,并且需要知道是哪个函数被调用了,根据被调用的函数执行不同的操作,这时候就需要给装饰器传参了
import time from functools import wraps # 定义一个装饰器 def firstCalled(*args, **kwargs): def wrapper_print_time(func): @wraps(func) def inner(): print(args) print(time.time()) func() return inner return wrapper_print_time @firstCalled('this is print_1') def print_1(): print(1) @firstCalled('this is print_2') def print_2(): print(2) print_1() print_2() #@firstCalled('this is print_1')这里首先等于@+firstCalled('this is print_1'), #就是先执行firstCalled('this is print_1'),所以把wrapper_print_time地址传了回来 #所以就等于@wrapper_print_time就跟上面不带参数的装饰器一样了 #不同的只是firstCalled('this is print_1')调用时先把参数传进去了