关于装饰器

装饰器的功能:

    装饰器作用就是给其他函数添加额外功能。

装饰器=高阶函数+函数嵌套+闭包

下面通过一段代码来了解一下装饰器的结构和使用方法:

 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的订单!

这种方式很少使用,最常用的还是前面的装饰器使用方式,应把主要精力放在前面装饰器模式的使用上。

多练习,多思考,越努力,越幸运!

原文地址:https://www.cnblogs.com/sun-10387834/p/10226461.html