装饰器的功能:
装饰器作用就是给其他函数添加额外功能。
装饰器=高阶函数+函数嵌套+闭包
下面通过一段代码来了解一下装饰器的结构和使用方法:
1 import time 2 #装饰器模板 3 def timer(func): 4 def wrapper(): 5 start_time=time.time() 6 func() 7 end_time=time.time() 8 print("函数执行时间:%s" %(end_time-start_time)) 9 return wrapper 10 11 #测试函数 12 @timer #等价于test=timer(test) 13 def test(): 14 time.sleep(2) 15 print("test函数执行完毕!") 16 17 #执行过程:存在缺点,因为每次都要先调用装饰器函数,在赋值给变量,麻烦!!! 18 # test=timer(test) 19 # # test() 20 21 #语法糖@ 22 res=test() 23 print(res)
那么现在有一个问题:如果被修饰函数有返回值怎么办?别着急,下面一段代码告诉你!
1 #Author : Kelvin 2 #Date : 2019/1/5 16:56 3 4 import time 5 #装饰器模板 6 def timer(func): 7 def wrapper(): 8 start_time=time.time() 9 res=func() 10 end_time=time.time() 11 print("函数执行时间:%s" %(end_time-start_time)) 12 return res 13 return wrapper 14 15 #测试函数 16 @timer #等价于test=timer(test) 17 def test(): 18 time.sleep(2) 19 print("test函数执行完毕!") 20 return "这是test的返回值!" 21 22 23 res=test() 24 print(res)
如果被修饰函数带参数又怎么办呢?继续......
1 #Author : Kelvin 2 #Date : 2019/1/5 17:39 3 4 import time 5 def timer(func): 6 def wrapper(*args,**kwargs): 7 start_time=time.time() 8 func(*args,**kwargs) 9 end_time=time.time() 10 print("函数执行时间:%s" %(end_time-start_time)) 11 return wrapper 12 13 @timer 14 def test(name): 15 time.sleep(2) 16 print("%s的test函数执行完毕!"%name) 17 18 res=test("kelvin") 19 print(res)
下面我们使用装饰器来完成一个简单的示例(用户登陆成功够才能执行函数内容)
1 #Author : Kelvin 2 #Date : 2019/1/5 19:10 3 4 def auth(func): 5 def wrapper(*args,**kwargs): 6 name=input("请输入用户名:").strip() 7 passwd=input("请输入密码:").strip() 8 if name=="kelvin" and passwd=="123": 9 res=func(*args,**kwargs) 10 return res 11 else: 12 print("用户名或密码错误!") 13 return wrapper 14 @auth 15 def index(): 16 print("首页!") 17 18 @auth 19 def home(name): 20 print("%s的个人首页!"%name) 21 22 @auth 23 def shopping_car(name): 24 print("%s购物车!"%name) 25 26 @auth 27 def order(name): 28 print("%s订单!"%name) 29 30 index() 31 home("kelvin") 32 order("kelvin") 33 # 输出结果 34 # 请输入用户名:kelvin 35 # 请输入密码:123 36 # 首页! 37 # 请输入用户名:kelvin 38 # 请输入密码:123 39 # kelvin的个人首页! 40 # 请输入用户名:kelvin 41 # 请输入密码:123 42 # kelvin订单!
根据上面代码,示例存在缺陷,因为每次执行函数都会要求用户输入用户名密码,那么怎么样才能让用户输入一次账号密码后就能随意执行其他函数呢?下面我们来使用装饰器模拟session的功能。
1 #Author : Kelvin 2 #Date : 2019/1/5 21:00 3 4 #已注册用户信息 5 users=[ 6 {"name":"kelvin","passwd":"123"}, 7 {"name":"elvin","passwd":"123"}, 8 {"name":"bob","passwd":"123"}, 9 {"name":"sun","passwd":"123"}, 10 ] 11 12 current_user={"name":None,"login":False} #模拟session存储用户登录信息 13 def auth(func): 14 def wrapper(*args,**kwargs): 15 if current_user["name"] and current_user["login"]: #如果session中有数据则说明用户已经登录,可以执行函数 16 res = func(*args, **kwargs) 17 return res 18 name=input("请输入用户名:").strip() #如果session中没有记录用户登录信息则要求用户输入账号密码 19 passwd=input("请输入密码:").strip() 20 for user in users: 21 if name==user["name"] and passwd==user["passwd"]: 22 current_user["name"]=name 23 current_user["login"]=True 24 res=func(*args,**kwargs) 25 return res 26 else: 27 print("用户名或密码错误!") 28 return wrapper 29 @auth 30 def index(): 31 print("欢迎进入首页!") 32 33 @auth 34 def home(name): 35 print("欢迎进入%s的个人首页!"%name) 36 37 @auth 38 def shopping_car(name): 39 print("查看%s的购物车!"%name) 40 41 @auth 42 def order(name): 43 print("查看%s的订单!"%name) 44 45 46 47 print(current_user) #登录前查看session中的信息 48 index() #执行index函数 49 print(current_user) #登陆后查看session中的信息 50 home("kelvin") #再次执行home函数可直接执行函数,不用输入账号密码 51 order("kelvin") #再次执行order函数可直接执行函数,不用输入账号密码 52 53 #输出结果: 54 # {'name': None, 'login': False} 55 # 请输入用户名:kelvin 56 # 请输入密码:123 57 # 欢迎进入首页! 58 # {'name': 'kelvin', 'login': True} 59 # 欢迎进入kelvin的个人首页! 60 # 查看kelvin的订单!
由于上面的代码中,用户信息直接放在列表中,现实中用户信息是放在各种不同的数据库中,所以如果装饰器能选择用户验证方式最好,怎么实现呢?看下面代码......
1 # Author : Kelvin 2 # Date : 2019/1/5 21:00 3 4 # 已注册用户信息 5 current_user = {"name": None, "login": False} # 模拟session存储用户登录信息 6 7 8 def auth_type(type="fileDB"): 9 def auth(func): 10 def wrapper(*args, **kwargs): 11 print("当前用户认证类型:",type) 12 if type == "fileDB": 13 if current_user["name"] and current_user["login"]: # 如果session中有数据则说明用户已经登录,可以执行函数 14 res = func(*args, **kwargs) 15 return res 16 name = input("请输入用户名:").strip() # 如果session中没有记录用户登录信息则要求用户输入账号密码 17 passwd = input("请输入密码:").strip() 18 for user in open("users", "r+"): 19 user = eval(user) 20 if name == user["name"] and passwd == user["passwd"]: 21 current_user["name"] = name 22 current_user["login"] = True 23 res = func(*args, **kwargs) 24 return res 25 else: 26 print("用户名或密码错误!") 27 else: 28 print("暂时不支持这种验证类型!") 29 30 return wrapper 31 32 return auth 33 34 35 @auth_type(type="fileDB") 36 def index(): 37 print("欢迎进入首页!") 38 39 40 @auth_type(type="fileDB") 41 def home(name): 42 print("欢迎进入%s的个人首页!" % name) 43 44 45 @auth_type(type="fileDB") 46 def shopping_car(name): 47 print("查看%s的购物车!" % name) 48 49 50 @auth_type(type="fileDB") 51 def order(name): 52 print("查看%s的订单!" % name) 53 54 55 print(current_user) # 登录前查看session中的信息 56 index() # 执行index函数 57 print(current_user) # 登陆后查看session中的信息 58 home("kelvin") # 再次执行home函数可直接执行函数,不用输入账号密码 59 order("kelvin") # 再次执行order函数可直接执行函数,不用输入账号密码 60 61 #输出结果: 62 # {'name': None, 'login': False} 63 # 当前用户认证类型: fileDB 64 # 请输入用户名:kelvin 65 # 请输入密码:123 66 # 欢迎进入首页! 67 # {'name': 'kelvin', 'login': True} 68 # 当前用户认证类型: fileDB 69 # 欢迎进入kelvin的个人首页! 70 # 当前用户认证类型: fileDB 71 # 查看kelvin的订单!