首页 > 代码库 > 装饰器

装饰器

装饰器定义:

本质是函数。函数的目的是为了完成特定的功能,那么装饰器的功能是什么呢?——饰器的功能是装饰其他函数。(为其他函数添加附加功能)。

装饰器的原则:装饰器对被它装饰的函数是完全透明的,即意味着用着被装饰的函数根本无法感知到装饰器。

  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()

 

装饰器