装饰器
Posted Zoe233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器相关的知识,希望对你有一定的参考价值。
装饰器定义:
本质是函数。函数的目的是为了完成特定的功能,那么装饰器的功能是什么呢?——饰器的功能是装饰其他函数。(为其他函数添加附加功能)。
装饰器的原则:装饰器对被它装饰的函数是完全透明的,即意味着用着被装饰的函数根本无法感知到装饰器。
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()
写一个装饰器:
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() #函数的调用方式不变
优化版(针对不同函数之间不同参数设定):
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(‘\033[32;1m User has passed authentication \33[0m‘) 18 return func(*args, **kwargs) 19 else: 20 exit(‘\033[32;1m Invalid username or password\033[0m‘) 21 elif auth_type==‘ldap‘: 22 print(‘ldap远程登陆认证‘) 23 24 25 26 return wrapper 27 return outer_wrapper 28 29 30 #首页,不需要登陆 31 def index(): 32 print(‘welcome to index page‘) 33 34 #用户主页,需要登陆 35 @auth(auth_type=‘local‘) 36 def home(): 37 print(‘welcome to home page‘) 38 return ‘from home‘ 39 40 #bbs论坛,需要登陆 41 @auth(auth_type=‘ldap‘) 42 def bbs(): 43 print(‘welcome to bbs page‘) 44 45 index() 46 home() 47 bbs()
以上是关于装饰器的主要内容,如果未能解决你的问题,请参考以下文章