装饰器,本质是函数,为其它函数添加附加功能。
装饰器对被装饰的函数没有任何影响,原则:
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
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() #这引用是不对的
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
分析:改变了函数的调用方式,违反原则。
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
分析:返回值调用不修改源代码和调用方式,但需求未实现。
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
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
若被修饰的函数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,所以报错。
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
总结:装饰器又叫语法糖。
用途:公司有网站很多页面,一个页面一个函数,现在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![0m 38 Great,home page got it! 39 Username:alex #执行bbs时需要再次输入用户名和密码 40 Password:abc123 41 [32:1mGreat,user got it![0m 42 Great,bbs page got it!
需求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![0m 46 Welcome to home page. 47 wrapper func: 48 搞毛线ldap,不会