装饰器:
定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能
需要遵从两条原则:
1.不能修改被装饰的所数的源代码
2.不能修改被装饰的函数的调用方式
例如有一段这样的代码
(1)
1. def test1(): 2. time.sleep(3) 3. print('this is test1')
现在有一个要求,不改动原有的代码统计这个函数的运行时间。你可能会这样写。
(2)
import time def test1(): time.sleep(3) print('this is test1') def func(test1): start_time=time.time() test1() stop_time=time.time() print("test1 run time is %s",stop_time-start_time) func(test1)
这样确实是在不改动原有的代码统计了test1的运行时间,但是这并不是装饰器,它没有遵从第二条原则不能修改被装饰的函数的调用方式,它调用的是func函数而并非test1函数。这样调用的不好之处就是,如果我们的程序中有多个地方用到了test1这个函数,我们使用这种方式扩展test1的功能时,我们就要在多个地方修改原有的代码,带来了极大的不便。
装饰器需要了解的知识:
1.函数即变量
2.高阶函数
a:把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能) 例如:(2)中的例子
b:返回值中包含函数名(不修改函数的调用方式)
import time def bar(): print('in the bar') def test2(func): print("扩展功能") return func bar=test2(bar) bar() #run bar
该案例利用test2函数为bar扩展了新功能,test2(bar)函数返回的是bar的地址,这样初步实现了不修改函数名就扩展了bar的功能。
3.嵌套函数
x=0 def grandpa(): x=1 def dad(): x=2 def son(): x=3 print(x) son() dad() grandpa()
嵌套函数要让它起作用,必须在当层调用否则嵌套函数或者它的子函数无效。例如案例中的dad()去除,那么dad函数和它下面的son函数都无效。
高阶函数+嵌套函数=装饰器
装饰器就很好的解决了之前提出的那个问题,在不改动原有的代码扩展功能。
import time def decorator(func): def warpper(): start_time=time.time() func() stop_time=time.time() print('the func run time is %s' %(stop_time-start_time)) return warpper @decorator #test1=decorator(test1) def test1(): time.sleep(3) print('this is test1') test1()
@decorator相当于test1=decorator(test1)
运行的流程大概是这样了
由于python是解释性语言,一行一行走下来并解释,首先走到decorator函数这里,由于没有这里还没有调用,前面说过函数即变量,就相当于把这个变量加载到内存当中,并不会走到里面。接着走到@decorator,由于@decorator修饰test1的,调用了decorator函数,并把test1的地址传入,这时func=test1,把warpper函数加载到内存当中,然后返回warpper函数的地址并赋值给test1,此时test1=warpper(偷梁换柱),最后执行test1(),也就是执行warper这个函数,计算开始时间,执行func(),这个func是之前传来的test1,没有被修改成warpper地址之前的,然后休眠3秒,得到时间差。
可以用debug来查看运行的过程,我也是看了好久才理明白。
如果函数需要传出参数,(一个或者多个)可以这样写。
import time def decorator(func): def warpper(*args,**kwargs): start_time=time.time() func(*args,**kwargs) stop_time=time.time() print('the func run time is %s' %(stop_time-start_time)) return warpper @decorator #相当于test1=decorator(test1) def test1(name1,name2): time.sleep(3) print('this is test1 %s %s',name1,name2) test1("arg1","arg2")
(*args,**kwargs)可以传任意个参数。