首页 > 代码库 > Day4-装饰器

Day4-装饰器

装饰器,本质是函数,为其它函数添加附加功能。

装饰器对被装饰的函数没有任何影响,原则:

1.不能修改被装饰的函数的源代码;2.不能修改调用方式;

高阶函数+嵌套函数,实现装饰器,准备知识如下:

1.函数和变量的区别:

变量存在内存中,比如x=1 ,1存在内存中,x就是内存的门牌号。若y=x,y也是一次引用,即y也是门牌号,清除变量1的话必须清除x和y。
def test(): #定义了test函数,即函数体存在内存中,test为门牌号
      pass
test = ‘函数体 ‘

python的内存回收器由python解释器回收的,清除变量通过清除所有门牌号的方式清除。

lambda匿名函数:没有起名,正是因为没有名字,它就没有门牌号,立马被python解释器回收掉
calc=lambdax:x*3
print(calc(3))
9

总结:函数就是变量,定义一个函数,等于把函数体给了函数名。变量有内存回收机制,函数一样。
先声明再调用,只要在调用之前声明就可以正常执行。因为声明是定义函数名,把函数体存在内存中。

2.高阶函数,满足的条件:

a.把一个函数名当做实参传给另外一个函数;(不修改被装饰函数源代码的情况下为其添加功能)
b.返回值中包含函数名(不修改函数的调用方式)

技术分享
 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print("in the bar")
 5 
 6 def test1(func):
 7     start_time = time.time()
 8     func()
 9     stop_time=time.time()
10     print("in the func run time is %s" %(start_time-stop_time))
11 
12 test1(bar)
13 结果:
14 in the bar
15 in the func run time is -3.014805555343628
View Code

3.嵌套函数:在def函数体内再def声明一个函数,叫嵌套函数

技术分享
 1 #函数的调用
 2 def test1():
 3     test2()  #函数的调用,不叫嵌套
 4 test1()
 5 
 6 #嵌套函数
 7 def foo():
 8     print("in the foo")
 9     def bar():
10         print("in the bar")
11     bar() #局部变量必须在函数内部调用
12 foo()
13 #bar()  #这引用是不对的
View Code

4.装饰器的形成步骤

a.改变函数调用方式:正常为test1(),变为deco(test1)

需求:计算test1和test2函数运行时间

技术分享
 1 import time
 2 def deco(func):
 3     start_time = time.time()
 4     func()
 5     stop_time = time.time()
 6     print("the func run time is %s" %(start_time-stop_time))
 7 def test1():
 8     time.sleep(3)
 9     print("in the test1")
10 def test2():
11     time.sleep(3)
12     print("in the test2")
13 deco(test1) #test1代表函数/变量,而test1()表示返回test1的运行结果
14 deco(test2)
15 执行结果:
16 in the test1
17 the func run time is -3.010805130004883
18 in the test2
19 the func run time is -3.012805461883545
View Code

分析:改变了函数的调用方式,违反原则。

b.返回值调用方式:func(),变成return func

技术分享
 1 import time
 2 def deco(func):
 3     start_time = time.time()
 4     return func #返回执行结果,下面语句就不执行了
 5     stop_time = time.time()
 6     print("the func run time is %s" %(start_time-stop_time))
 7 def test1():
 8     time.sleep(3)
 9     print("in the test1")
10 def test2():
11     time.sleep(3)
12     print("in the test2")
13 test1 = deco(test1)
14 test1()
15 test2 = deco(test2)
16 test2()
17 
18 执行结果:
19 in the test1
20 in the test2
View Code

分析:返回值调用不修改源代码和调用方式,但需求未实现。

c.加了嵌套函数:函数中定义新函数,返回新函数的变量名=内存地址=函数名,把返回值赋予一个变量,执行变量即可得到结果

技术分享
 1 import time
 2 def timer(func):#func=test1,把test1函数作为timer函数的参数传递进去
 3     def deco(): #函数timer中定义新函数deco,即为函数嵌套
 4         start_time = time.time()
 5         func() #func=test1,运行func(),即为运行test1()
 6         stop_time = time.time()
 7         print("the func run time is %s"%(start_time-stop_time))
 8     return deco #返回deco内存地址,即为高级函数
 9 def test1():
10     time.sleep(3)
11     print("in the test1")
12 test2 = timer(test1) #把test1()函数的内存地址test1作为参数传递给timer函数
13 test2() #执行test2()函数
14 
15 执行结果:
16 in the test1
17 the func run time is -3.010805130004883
View Code

d.装饰器

技术分享
 1 import time
 2 def timer(func):
 3     def deco():
 4         start_time = time.time()
 5         func()
 6         stop_time = time.time()
 7         print("the fun run time is %s"%(start_time-stop_time))
 8     return deco
 9 
10 @timer #给test1增加新功能
11 def test1():
12     time.sleep(3)
13     print("in the test1")
14 
15 test1() #执行deco函数时,嵌套执行的是func()函数,即test1()=func()
16 
17 执行结果:
18 in the test1
19 the fun run time is -3.0118050575256348
View Code

若被修饰的函数test1带位置参数,会咋样?

技术分享
 1 @timer
 2 def test2(name):
 3     time.sleep(2)
 4     print("in the test2",name)
 5 test2()
 6 
 7 执行结果:报错
 8 TypeError: test2() missing 1 required positional argument: name
 9 分析:
10 test2()相当于函数timer()中的func()函数,在嵌套函数deco()中执行时func()没有位置参数name,所以报错。
View Code

e.装饰器:举例说明带可变参数和不带参数

技术分享
 1 import time
 2 def timer(func):
 3     def deco(*args,**kwargs):
 4         start_time = time.time()
 5         func(*args,**kwargs)
 6         stop_time = time.time()
 7         print("the fun run time is %s"%(stop_time-start_time))
 8     return deco
 9 
10 @timer
11 def test1():
12     time.sleep(2)
13     print("in the test1")
14 @timer
15 def test2(name,age):
16     time.sleep(2)
17     print("in the test2",name,age)
18 
19 test1()
20 test2(alex,22)
21 
22 执行结果:
23 in the test1
24 the fun run time is 2.0124034881591797
25 in the test2 alex 22
26 the fun run time is 2.0124034881591797
View Code

 

总结:装饰器又叫语法糖。

用途:公司有网站很多页面,一个页面一个函数,现在100个页面里有20个页面需要登录才能看到,所以在20个页面函数里加上验证功能才能看到。

需求1:编写一个登陆认证程序,主页index不需要登录,用户自己的home和bbs需要登录

技术分享
 1 #!/usr/bin/env python
 2 import time
 3 user,passwd = alex,abc123
 4 def auth(func):
 5     def wrapper(*args,**kwargs):
 6         username = input("Username:").strip()
 7         password = input("Password:").strip()
 8 
 9         if user == username and passwd == password:
10             print("\033[32:1mGreat,user got it!\033[0m")
11             return func(*args,**kwargs)
12         else:
13             exit("\033[31:1mInvalid username or password\033[0m")
14             #exit()
15     return wrapper
16 
17 def index():
18     print("Welcome to index page.")
19 
20 @auth
21 def home():
22     print("Great,home page got it!")
23     return "from home"
24 
25 @auth
26 def bbs():
27     print("Great,bbs page got it!")
28 
29 index()
30 home()
31 bbs()
32 
33 执行结果:
34 Welcome to index page.
35 Username:alex #执行home()需要输入用户名和密码
36 Password:abc123
37 [32:1mGreat,user got it!
38 Great,home page got it!
39 Username:alex  #执行bbs时需要再次输入用户名和密码
40 Password:abc123
41 [32:1mGreat,user got it!
42 Great,bbs page got it!
View Code

需求2,终极版:home实现本地认证,bbs实现ldap认证

技术分享
 1 import time
 2 user,passwd = alex,abc123
 3 def auth(auth_type):
 4     print("auth func:",auth_type)
 5     def out_wrapper(func):
 6         def wrapper(*args,**kwargs):
 7             print("wrapper func:",*args,**kwargs)
 8             if auth_type == "local":
 9                 username = input("Username:").strip()
10                 password = input("Password:").strip()
11 
12                 if user == username and passwd == password:
13                     print("\033[32:1mGreat user got it!\033[0m")
14                     return func(*args,**kwargs)
15                 else:
16                     exit("\033[31:1mInvaild username or password\033[0m")
17             elif auth_type == "ldap":
18                 print("搞毛线ldap,不会")
19         return wrapper
20     return out_wrapper
21 
22 def index():
23     print("Welcome to index page.")
24 
25 @auth(auth_type="local")#这句话相当于home() = wrapper(),接下来home才开始执行
26 def home():
27     print("Welcome to home page.")
28     return "From home"
29 
30 @auth(auth_type="ldap")
31 def bbs():
32     print("Welcome to bbs page.")
33 
34 index()
35 home()
36 bbs()
37 
38 执行结果:
39 auth func: local
40 auth func: ldap
41 Welcome to index page.
42 wrapper func:
43 Username:alex
44 Password:abc123
45 [32:1mGreat user got it!
46 Welcome to home page.
47 wrapper func:
48 搞毛线ldap,不会
View Code

 

Day4-装饰器