装饰器
装饰器本质上就是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离大量与函数功能本身无关的雷同代码并继续重用。概括讲,装饰器的作用就是为依据存在的对象添加额外的功能。
定义:本质是函数,(装饰其他函数)就是为其他函数添加附加功能
原则:
- 不能修改被装饰的函数的源代码( 再不修改被装饰函数源代码的情况下为其添加功能)
- 不能修改被装饰的函数的调用方式
实现装饰器功能知识储备:
- 函数既“变量”
- 函数的参数可以接收变量,也可以接受函数,这里引用函数即变量的概念
在python里面函数就是一个变量,函数名就是一个变量,这个函数名里面存的是这个函数的内存地址,它把函数体放到内存里,在调用的时候从函数名里面的这个内存地址找到函数体然后运行这个函数。前面的博客说函数的时候,说过函数名后面加上小括号就是调用这个函数,如果只写这个函数名的话,打印一下就是这个函数的内存地址。
1 def test(): 2 print('Hello Word') 3 4 5 print(test) # 打印函数的内存地址 #<function test at 0x100662e18> 6 test() # 调用函数
- 高阶函数
- 把一个函数名当做实参传给另外一个函数
- 返回值中包含函数名(不修改函数的调用方式) *是不是不理解很懵逼,没事,一会用到*
- 当传递函数作为变量时,被称为高阶函数
1 import time 2 def bar(): 3 time.sleep(3) 4 print('in the bar !!') 5 6 def test1(func): 7 start_tiem = time.time() 8 func()# run bar() 9 stop_time = time.time() 10 print('the func run time is %s' %(stop_time-start_tiem)) 11 test1(bar)
强行插入知识点: 嵌套函数
1 def fun(): 2 print('in fun ') 3 def bar(): #函数的嵌套 4 print('in the bar ') 5 bar() 6 fun() 7 #--------------------------# 8 name = "Baylor" 9 def change_name(): 10 11 name = "Baylor2" 12 def change_name2(): 13 name = "Baylor3" 14 print("第3层打", name) 15 16 change_name2() # 调用内层函数 17 print("第2层打印", name) 18 19 change_name() 20 print("最外层打印", name)
嵌套函数中的作用域顺序-由内而外的寻找符合要求的参数
第一个装饰器
但每一次都需要从新对 test1 进行函数赋值,我们只需要通过装饰器的特殊书写方式,在需要增加新功能的函数上增加“@装饰器函数名”就可以完成装饰器的使用
1 def timer(func): 2 def deco():#函数的嵌套 timer(test1) func = test1 3 start_time = time.time() 4 func() #run test1 5 stop_time = time.time() 6 print(' the func time is %s'%(stop_time - start_time)) 7 return deco #高阶函数- 返回值中包含函数名 8 9 @timer # test1 = timer(test1) 10 def test1(): 11 time.sleep(3) 12 print('in the test1') 13 @timer 14 def test2(): 15 time.sleep(2) 16 print('in the test2') 17 18 #test1 = timer(test1) 19 test1()# --->deco 20 test2() 21 print(timer(test1)) #打印test1 内存地址
所以我们说:高阶函数+嵌套函数 =>装饰器 ,我们通过高阶函数+函数嵌套实现了以上的装饰器,他符合不改变调用方式,为函数增加新功能。
问题:被装饰的函数如果有参数呢?
1 def w1(func): 2 def inner(arg): 3 # 验证1 4 # 验证2 5 # 验证3 6 return func(arg) 7 return inner 8 9 @w1 10 def f1(arg): 11 print ('f1') 12 13 14 def w1(func): 15 def inner(arg1,arg2,arg3): 16 # 验证1 17 # 验证2 18 # 验证3 19 return func(arg1,arg2,arg3) 20 return inner 21 22 @w1 23 def f1(arg1,arg2,arg3): 24 print ('f1')
问题:可以装饰具有处理n个参数的函数的装饰器?
1 def w1(func): 2 def inner(*args, **kwargs): 3 # 验证1 4 # 验证2 5 # 验证3 6 print(*args,**kwargs) 7 8 return func(*args, **kwargs) 9 return inner 10 11 @w1 12 def f1(arg1, arg2, arg3): 13 print('f1') 14 15 f1(1,2,3)
如果要装饰的函数带有参数时,因为你也不知道到底被装饰的函数会传什么参数,所以可以使用可变参数和关键字参数来接收所有的参数
1 import time 2 def timer(func): 3 def deco(*args,**kwargs):#函数的嵌套 timer(test1) func = test1 4 start_time = time.time() 5 func(*args,**kwargs) #run test1 6 stop_time = time.time() 7 print(' the func time is %s'%(stop_time - start_time)) 8 return deco #高阶函数 9 10 @timer # test1 = timer(test1) 11 def test1(): 12 time.sleep(3) 13 print('in the test1') 14 @timer # test2 = timer(test2) = deco test2(name) = deco(name) 15 def test2(name,arg): 16 time.sleep(2) 17 print('in the test2 %s %s',name,arg) 18 19 test1()# --->deco 20 test2('Baylor',28)
在函数的传递参数过程中,我们的装饰器不能满足带参数的传递,以上,实现了当函数有参数传递的过程中也可以实现装饰器的新功能
终极函数装饰-补充知识
1 import time 2 user,passwd = 'jinyu','abc123' 3 def auth(auth_type): 4 def outer_wrapper(func): 5 print('auth func:',auth_type) 6 def wrapper(*args,**kwargs): 7 print('------',auth_type) 8 if auth_type =='local': 9 username = input('Username:') 10 password = input('Password') 11 if user == username and passwd == password: 12 13 print('