装饰器定义:
本质是函数。函数的目的是为了完成特定的功能,那么装饰器的功能是什么呢?——饰器的功能是装饰其他函数。(为其他函数添加附加功能)。
装饰器的原则:装饰器对被它装饰的函数是完全透明的,即意味着用着被装饰的函数根本无法感知到装饰器。
1.不能修改被装饰的函数的源代码
2.不能修改被装饰的函数的调用方式
——如,用装饰器将add()函数变为减法函数,但是add()还是认为自己是个加法函数。
装饰器的知识储备:
1.函数即“变量”
2.高阶函数
3.嵌套函数
高阶函数+嵌套函数-->装饰器
1.函数即“变量”
函数即“变量”在内存中的存储引用和清空机制:
1 #本段代码是为了说明函数即变量,另外函数是如何在内存中存储,调用和清除的。 2 #第一段代码: 3 def foo(): 4 print('in the foo') 5 bar() 6 7 foo() #调用foo(),在内存中由于没有bar的函数体,也没有定义相应的函数bar,所以会报错:NameError: name 'bar' is not defined 8 9 #第二段代码: 10 def bar(): 11 print('in the bar') 12 def foo(): 13 print('in the foo') 14 bar() 15 16 foo() #调用foo(),返回"in the foo" "in the bar" 17 18 #第三段代码: 19 def foo(): 20 print('in the foo') 21 bar() 22 def bar(): 23 print('in the bar') 24 25 foo() #调用foo(),返回"in the foo" "in the bar" 26 27 28 #第四段代码: 29 def foo(): 30 print('in the foo') 31 bar() 32 33 foo() # 调用foo() 34 35 def bar(): 36 print('in the bar') 37 #返回结果报错,NameError: name 'bar' is not defined
下图是关于函数和变量在内存中的存储,调用和清除的图示。
综上所述,函数的函数体作为字符串的形式存放在内存开辟的一个空间内,当其赋值给一个函数名或变量时,在内存中存放着;当函数调用时,则执行在内存中的函数体;但函数没有函数名(如lambda函数),并且没有相应的变量与之相对应时,函数体在内存中会消失。
另外调高阶函数时,无论函数的定义的顺序,只要是在调用之前,内存中都有相应的函数体和函数对应,则调用就不会出错。如果在调用函数前,内存中并没有相应的函数或变量,则一定会报错。
2.高阶函数
满足下列两个条件之一的就是高阶函数:
a.把一个函数名当作实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
b.返回值中包含函数名(不修改函数的调用方式)
a实例
1 import time 2 def bar(): #被装饰的函数 3 time.sleep(3) 4 print('in the bar') 5 6 def test1(func): #装饰函数,也是高阶函数,这个高阶函数实现了在不修改被装饰的函数bar的源代码的情况下为其增加了新功能 7 start_time=time.time() #在func()之前记录的是func()开始的时间 8 func() 9 stop_time=time.time() #在func()之后,记录的是func()运行结束的时间 10 print('the func run time is %s'%(stop_time-start_time)) #统计func()运行的总时长 11 12 test1(bar)
返回:
b实例:
1 import time 2 def bar(): 3 time.sleep(3) 4 print('in the bar') 5 6 def test2(func): 7 print(func) 8 return func 9 10 bar=test2(bar) #将函数bar的门牌号取下另外赋值,bar代表的是test2(bar) 11 bar() #函数bar的调用方式未发生变化
返回:
3.嵌套函数
定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)
表现的形式一定是在def函数下继续嵌套定义def函数。如下面的实例:
1 def foo(): 2 print('in the foo') 3 def bar(): 4 print('in the bar') 5 bar() #嵌套函数中想要通过foo()直接调用内部嵌套的函数,必须在内部调用bar() 6 7 foo()
返回:
以上这个案例仅仅是嵌套函数。在函数内想要通过调用最外层的函数自动触发内部定义的函数需要在内部的最外层调用内层的函数。并且调用是逐层调用的,在上面的案例中因为仅有两层,所以仅调用了内部的bar().
写一个装饰器:
1 import time 2 def timer(func): #timer(test1) 高阶函数+嵌套函数==>装饰器 3 def deco(): 4 start_time=time.time() 5 func() #run test1() 6 stop_time=time.time() 7 print('the func run time is %s'%(stop_time-start_time)) 8 return deco #在函数timer(func)中直接返回deco,返回deco的内存地址,供test1调用。 9 10 @timer #等价于test1=timer(test1) 11 def test1(): #函数的源代码不变 12 time.sleep(3) 13 print('in the test1') 14 15 test1() #函数的调用方式不变
通过debugger断点,上面这个简单的装饰器的执行顺序为:
1.执行import time 2.def timer(func): 3.@timer #调用装饰器 4.def deco(): #执行装饰器timer(),先执行def deco(),再执行return deco 5.return deco #返回deco的内存地址 6.test1() #调用函数test1() 8.start_time=time.time() #开始执行传入的deco内存地址的内容,执行deco()的代码 9.func() #调用test1(),执行test1() 10.time.sleep(3) #执行test1() 11.print('in the test1') #执行test1() 12.stop_time=time.time() #执行test1()完毕,回到deco(),执行deco后面的命令 13.print('the func run time is %s'%(stop_time-start_time)) 结束。
优化版(针对不同函数之间不同参数设定):
1 import time 2 def timer(func): #timer(test1) 高阶函数+嵌套函数==>装饰器 3 def deco(*args,**kwargs): #在deco上传入不固定参数 4 start_time=time.time() 5 func(*args,**kwargs) #调用不固定参数的函数 6 stop_time=time.time() 7 print('the func run time is %s'%(stop_time-start_time)) 8 return deco #在函数timer(func)中直接返回deco,返回deco的内存地址,供test1调用。 9 10 #等价于test1=timer(test1) 11 @timer 12 def test1(): #函数的源代码不变 13 time.sleep(3) 14 print('in the test1') 15 16 @timer 17 def test2(name): 18 print('test2:',name) 19 20 test1() #函数的调用方式不变 21 test2('Zoe')
只是优化了参数,执行顺序和上面的装饰器一致。
终极版:
1 '''需求: 2 一个网站的页面代表一个函数,现在设定指定数量的页面需要验证登录''' 3 4 import time 5 6 #定义auth装饰器,用于页面登陆 7 user,passwd='zoe','123456' 8 def auth(auth_type): 9 print('auth func',auth_type) 10 def outer_wrapper(func): 11 def wrapper(*args, **kwargs): 12 print('wrapper func args',) 13 if auth_type=='local': 14 username = input('Username:'.strip()) 15 password = input('Password:'.strip()) 16 if user == username and passwd == password: 17 print('