day4-装饰器
装饰器定义
首先装饰器实现的条件:高阶函数+嵌套函数 =》装饰器
装饰器实现过程
在不修改源代码的情况下,给test1,2附加功能:
import time def test1(): time.sleep(3) print("in the test1") def test2(): time.sleep(3) print("in the test2") test1() test2()
1.加入高阶函数
import time def deco(func): start_time=time.time() func() stop_time=time.time() print("the func run time is %s" %(stop_time-start_time)) def test1(): time.sleep(3) print("in the test1") def test2(): time.sleep(3) print("in the test2") deco(test1) deco(test2)
尝试1:我们要求不改变调用方式,使用高阶函数的第二种方式——>返回值中包含函数名
def deco(func): start_time=time.time() return func #程序运行至此return语句停止 stop_time=time.time() print("the func run time is %s" %(stop_time-start_time)) def test1(): time.sleep(3) print("in the test1") def test2(): time.sleep(3) print("in the test2") test1 = deco(test1) test1 () test2 = deco(test2) test2 () #输出 in the test1 in the test2 #没有改变源代码,没有改变调用方式,但是功能没有附加
2.加入嵌套函数
尝试2:因为尝试1没有附加功能,在这里我们加入了嵌套函数
import time def timer(func): #timer(test1) func=test1 def deco(): start_time=time.time() func() #run test1() stop_time=time.time() print("the func run time is %s"%(stop_time-start_time)) return deco def test1(): time.sleep(3) print("in the test1") def test2(): time.sleep(3) print("in the test2") test1=timer(test1) test1() #---->实际上是在执行deco() #输出 in the test1 the func run time is 3.0035839080810547 #当我们执行test1()时,除了执行了test1(),还附加了其他的功能
装饰器的调用
在Python中,它给我们通过另一种方式调用装饰器,想在哪个函数上附加功能,就在该函数的头部加上@timer
import time def timer(func):#timer(test1) func=test1 def deco(): start_time=time.time() func() #run test1() stop_time=time.time() print("the func run time is %s"%(stop_time-start_time)) return deco @timer def test1(): #该函数调用装饰器,实现附加功能 time.sleep(3) print("in the test1") @timer def test2(): time.sleep(3) print("in the test2") test1() test2() #输出 in the test1 the func run time is 3.0037789344787598 in the test2 the func run time is 3.0018179416656494
1.执行函数带参数
如果我们要在test函数里面传参数的话,我们可以:
import time def timer(func): #timer(test2) func=test2 def deco(arg1): start_time=time.time() func(arg1) #run test2() stop_time=time.time() print("the func run time is %s"%(stop_time-start_time)) return deco @timer #相当于test1=timer(test1), test1()=deco() def test1(): time.sleep(3) print("in the test1") @timer #相当于test1=timer(test1), test1()=deco() def test2(name): time.sleep(3) print("in the test2") test2(“dick”) # 由于我们执行test2("dick")是执行deco(),所以我们要把一个参数传进去 #输出 in the test1: dick the func run time is 3.0016181468963623
但是,我们装饰的函数是千奇百怪的,所以我们要把不止一个参数传进去,怎么办?
import time def timer(func): #timer(test2) func=test2 def deco(*args,**kwargs): start_time=time.time() func(*args,**kwargs) #run test2() stop_time=time.time() print("the func run time is %s"%(stop_time-start_time)) return deco @timer #相当于test1=timer(test1), test1()=deco() def test1(): time.sleep(3) print("in the test1") @timer #相当于test1=timer(test1), test1()=deco() def test2(name,age): time.sleep(3) print("in the test2",name,age) test1(“dick”,22) test2() #输出 in the test1: dick 22 the func run time is 3.001953125 in the test2 the func run time is 3.004183053970337
最后,我们通过一个例子来验证一下:
import time user,passwd="huwei","qawsed" def auth(func): def wrapper(*args,**kwargs): #2 username=input("Username:") password=input("Password:") if user==username and password==passwd: print("\033[32;1mUser has passed authenticate!\033[0m") func(*args,**kwargs) #1 “from home” else: exit("\033[31;1mInvalid user name or password!\033[0m") return wrapper def index(): print("Welcome to index page") @auth def home(): print("welcome to homepage") return"from home" @auth def bbs(): print("bbs page") index() print(home()) #home()---->实际上是在执行#2处的wrapper() bbs() #输出 Welcome to index page Username:huwei Password:qawsed User has passed authenticate! welcome to home page None #home返回的结果不见了,我们虽然没有改变源代码和调用方式,但是在这里,代码的返回结果改变了,怎么办?
解析:说到底其实是,#1处的func()调用的是home(),#1处没有返回的func,所以#1处的func()执行结果没有传给任何变量.而调用home()其实是在调用#2处的wrapper(),那么wrapper()执行了,没有返回值,只有内存地址,当然为None
2.执行函数有返回值
所以在#1处我们做如下修改:
return func(*args,**kwargs) #或赋给一个变量,如: res = func(*args,**kwargs) print("=========") return res
再看看我们的代码:
import time user,passwd="huwei","qawsed" def auth(func): def wrapper(*args,**kwargs): #2 username=input("Username:") password=input("Password:") if user==username and password==passwd: print("\033[32;1mUser has passed authenticate!\033[0m") res = func(*args,**kwargs) print("=========") return res else: exit("\033[31;1mInvalid user name or password!\033[0m") return wrapper def index(): print("Welcome to index page") @auth def home(): print("welcome to home page") return"from home" @auth def bbs(): print("bbs page") index() print(home()) #home()----》实际上是在执行#2处的wrapper() bbs() #输出 Welcome to index page Username:huwei Password:qawsed User has passed authenticate! welcome to home page from home
但是在现实情况中,认证方式会有多种,ssh key认证,ldap认证,本地认证等等,那如何实现?再加一个认证装饰器?
答:我们可以再加一层,同时print()参数,看看传入的究竟是什么?
3.带参数的装饰器
import time user,passwd="huwei","qawsed" def auth(func):#3 print("auth func:",func) #4 def out_wrapper(): #5 新加入的一层 def wrapper(*args,**kwargs): #2 print("wrapper func args:",*args,**kwargs) username=input("Username:") password=input("Password:") if user==username and password==passwd: print("\033[32;1mUser has passed authenticate!\033[0m") func(*args,**kwargs) #1 “from home” else: exit("\033[31;1mInvalid user name or password!\033[0m") return wrapper return out_wrapper def index(): print("Welcome to index page") @auth(auth_type="local") def home(): print("welcome to home page") return"from home" @auth(auth_type="ldap") def bbs(): print("bbs page") index() home() bbs() #输出 @auth(auth_type="local") TypeError: auth() got an unexpected keyword argument 'auth_type'
根据输出结果,我们将#3和#4处改为auth_type
def auth(auth_type):#3 print("auth func:",auth_type) #4 #输出 @auth(auth_type="local") #auth func: local #可以看到验证方式已经传进来了 TypeError: out_wrapper() takes 0 positional arguments but 1 was given
刚刚我们的#3处的func移下来了被我们删除了,其实我们删除的func传到#5处了(整体往下移了一层),接着我们将func在#5处传进来加上
import time user,passwd="huwei","qawsed" def auth(auth_type): print("auth func:",auth_type) def out_wrapper(func): def wrapper(*args,**kwargs): print("wrapper func args:",*args,**kwargs) username=input("Username:") password=input("Password:") if user==username and password==passwd: print("\033[32;1mUser has passed authenticate!\033[0m") return func(*args,**kwargs) #return func()相当于home() else: exit("\033[31;1mInvalid username or password!\033[0m") return wrapper return out_wrapper def index(): print("Welcome to index page") @auth(auth_type="local") def home(): #6 home = wrapper ————>>>home()相当于wrapper(),当执行home()时,直接执行wrapper函数 print("welcome to homepage") return"from home" @auth(auth_type="ldap") def bbs(): print("bbs page") #输出 Auth func: local Auth func: ldap Welcome to index page wrapper func args: Username:huwei Password:qawsed User has passed authenticate! welcome to home page wrapper func args: Username:huwei Password:qawsed User has passed authenticate! bbs page
如果我需要根据不同的参数输入执行不同的验证方式,那么:
import time user,passwd="huwei","qawsed" def auth(auth_type): print("auth func:",auth_type) def out_wrapper(func): def wrapper(*args,**kwargs): print("wrapper func args:",*args,**kwargs) if auth_type=="local": username=input("Username:") password=input("Password:") if user==username and password==passwd: print("\033[32;1mUser has passed authenticate!\033[0m") return func(*args,**kwargs) else: exit("\033[31;1mInvalid username or password!\033[0m") elif auth_type=="ldap": print("Access LDAP server.......") return wrapper return out_wrapper def index(): print("Welcome to index page") @auth(auth_type="local") def home(): print("welcome to home page") return"from home" @auth(auth_type="ldap") def bbs(): print("bbs page") index() home() bbs() #输出 auth func: local auth func: ldap welcome to index page wrapper func args: username:huwei password:qawsed user has passed authenticate! welcome to home page wrapper func args: Access LDAP server.......