【装饰器介绍】

1 1.概念:
2 在不改变被装饰对象原代码和调用方式!!!的情况下给被装饰对象添加新的功能!!!
3 2.本质:
4 并不是一门新的技术 而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果
5 3.口诀:
6 对修改封闭 对扩展开放

推导过程

需求: 

  在不修改函数的源代码以及调用方式的前提下为其添加统计运行时间功能

 

 第一种:

 失败:修改了源代码,没有修改调用方式,没有实现装饰器的原理

方案二:

 但是出现新的问题:index可能会在其他地方出现,每次都要重写那三行代码,所以不好,代码冗余

方案三:

 失败:代码冗余问题解决了,却修改了源代码的调用方式,而且index被写死了

=============================================================

方案三的优化1

 1 import time
 2 
 3 
 4 def index(x, y):
 5     time.sleep(2)
 6     print('index %s %s' % (x, y))
 7 
 8 
 9 # 装饰功能函数
10 def wrapper(*args, **kwargs):
11     start = time.time()
12     index(*args, **kwargs)  # 写活了index(11,22),*和**可以接收任何形式传参方式
13     end = time.time()
14     print(end - start)
15 
16 
17 wrapper(11, 22)

写活了index,但是只能用来装饰index,如果后期还想装饰其他功能,怎么办????

======================================================================

方案三继续优化2:

 1 import time
 2 
 3 
 4 def index(x, y):
 5     time.sleep(2)
 6     print('index %s %s' % (x, y))
 7 
 8 
 9 def outer(func):
10     # func=index  # func=被装饰的内存地址
11     # 装饰功能函数
12     def wrapper(*args, **kwargs):
13         start = time.time()
14         func(*args, **kwargs)
15         end = time.time()
16         print(end - start)
17 
18     return wrapper  # 返回的wrapper是因为当初wrapper是全局的,但是我们把它缩进了一个函数内,如果想全局使用,必须返回
19 
20 
21 index = outer(index)  # f=outer(index的内存地址)
22 index(11,22)

在优化1的基础上把被装饰对象写活了

 ==========================================

继续优化,这时候还可以装饰其他功能

 1 import time
 2 
 3 
 4 def index(x, y):
 5     time.sleep(2)
 6     print('index %s %s' % (x, y))
 7 
 8 
 9 # 装饰home
10 def home(name):
11     time.sleep(3)
12     print('home %s' % name)
13 
14 
15 def outer(func):
16     # 装饰功能函数
17     def wrapper(*args, **kwargs):
18         start = time.time()
19         func(*args, **kwargs)
20         end = time.time()
21         print(end - start)
22 
23     return wrapper
24 
25 
26 index = outer(index)
27 home = outer(home)
28 home(name='Alice')

================================================================================

方案三的优化3:最终优化

 1 import time
 2 
 3 
 4 def index(x, y):
 5     time.sleep(2)
 6     print('index %s %s' % (x, y))
 7 
 8 
 9 # 装饰home
10 def home(name):
11     time.sleep(3)
12     print('home %s' % name)
13     return 123  # home有返回值
14 
15 
16 def outer(func):
17     # 装饰功能函数
18     def wrapper(*args, **kwargs):
19         start = time.time()
20         res=func(*args, **kwargs)
21         end = time.time()
22         print(end - start)
23         return res  # 伪装的和home一样了,就有返回值了
24 
25     return wrapper
26 
27 
28 index = outer(index)
29 home = outer(home)
30 res=home('Alice')
31 print('返回值===',res)
32 # 返回值=== None,其实这时候的home已经不是之前的home了,而是经过装饰后的wrapper函数,把wrapper函数进一步封装,就做的和home一样了,有返回值了

无参装饰器模板

1 # 务必掌握
2 def outer(func):
3     def inner(*args, **kwargs):
4         # 执行被装饰对象之前可以做的额外操作
5         res = func(*args, **kwargs)
6         # 执行被装饰对象之后可以做的额外操作
7         return res
8     return inner

 装饰器语法糖

 1 def outer(func_name):
 2     def inner(*args, **kwargs):
 3         print('执行被装饰对象之前可以做的额外操作')
 4         res = func_name(*args, **kwargs)
 5         print('执行被装饰对象之后可以做的额外操作')
 6         return res
 7     return inner
 8 """
 9 语法糖会自动将下面紧挨着的函数名当做第一个参数自动传给@后面的函数调用运行
10 并用一个和函数名一样的变量名,去接收 @后面的函数运行的返回值!!!
11 """
12 @outer  # func = outer(func)
13 def func():
14     print('from func')
15     return 'func'
16 
17 @outer  # index = outer(index)
18 def index():
19     print('from index')
20     return 'index'
21 
22 func()
23 index()

===============================================================================

登录功能案例

 1 1.编写一个用户认证装饰器
 2   函数:register login transfer withdraw 
 3   基本要求
 4         执行每个函数的时候必须先校验身份 eg: jason 123
 5   拔高练习(有点难度)
 6         执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
 7   提示:全局变量 记录当前用户是否认证
 8 
 9 # 定义一个变量记录用户的登录状态
10 is_login = False
11 
12 
13 def login_auth(func_name):
14     def inner(*args, **kwargs):
15         global is_login
16         # 先判断全局名称空间中的变量名is_login绑定的值是否为True
17         if is_login:
18             res = func_name(*args, **kwargs)
19             return res
20         username = input('username>>>:').strip()
21         password = input('password>>>:').strip()
22         if username == 'jason' and password == '123':
23             # 将全局名称空间中记录用户登录状态的数据值该为True
24             is_login = True
25             res = func_name(*args, **kwargs)
26             return res
27         else:
28             print('用户名或密码错误无法执行函数')
29     return inner
30 
31 
32 @login_auth
33 def register():
34     print('注册功能')
35 
36 
37 @login_auth
38 def login():
39     print('登录功能')
40 
41 
42 @login_auth
43 def shopping():
44     print('购物功能')
45 
46 
47 register()
48 login()
49 shopping()

 。

==========================================================有参装饰器,无语法糖

山炮玩法

 1 print('---有参函数装饰器:无语法糖---')
 2 
 3 
 4 def outter(func, db_type):  # 没用语法糖@xx,所以可以加参数
 5     def wrapper(*args, **kwargs):
 6         name = input('your name :').strip()
 7         pwd = input('your password;').strip()
 8         # 但是账号密码可能来源与很多地方,这时候就要判断
 9         if db_type == 'file':  # file是文件的意思  但需要传参
10             print('基于文件的认证')  # 下面运行输入的账号密码就是文件的认证
11             if name == 'jh' and pwd == '123':
12                 res = func(*args, **kwargs)
13                 return res
14             else:
15                 print('1')
16         elif db_type == 'mysql':  # mysql是数据库的意思
17             print('2')
18         elif db_type == 'ldap':
19             print('3')
20         else:
21             print('不支持其他认证')
22 
23     return wrapper
24 
25 
26 def index(x, y):
27     print('index->>%s:%s' % (x, y))
28 
29 
30 def home(name):
31     print('home->>%S' % name)
32 
33 
34 def transfer():
35     print(transfer)
36 
37 
38 index = outter(index, 'file')
39 home = outter(home, 'mysql')
40 transfer = outter(transfer, 'ldap')
41 # index(1, 2)
42 # home('jh')
43 transfer()

---------------------------------------------------复杂,代码冗余

升级,有语法糖

 1 def auth(db_type):  # 他没有被限制死,还是可以增加新功能的,不用再套函数了
 2     def deco(func):
 3         def wrapper(*args, **kwargs):
 4             name = input('your name :').strip()
 5             pwd = input('your password;').strip()
 6             # 但是账号密码可能来源与很多地方,这时候就要判断
 7             if db_type == 'file':  # file是文件的意思  但需要传参
 8                 print('基于文件的认证')  # 下面运行输入的账号密码就是文件的认证
 9                 if name == 'jh' and pwd == '123':
10                     res = func(*args, **kwargs)  # index(1,2)
11                     return res
12                 else:
13                     print('1')
14             elif db_type == 'mysql':  # mysql是数据库的意思  # 这时候db需要
15                 print('2')
16             elif db_type == 'ldap':
17                 print('3')
18             else:
19                 print('不支持其他认证')
20 
21         return wrapper
22 
23     return deco
24 
25 
26 @auth(db_type='file')  # 原本是@deco # 现在index=deco(index) 调deco,返回wrapper index=wrapper
27 def index(x, y):
28     print('index->>%s:%s' % (x, y))
29 
30 
31 @auth(db_type='mysql')
32 def home(name):
33     print('home->>%s' % name)
34 
35 
36 @auth(db_type='ldap')
37 def transfer():
38     print('transfer')
39 
40 
41 # index(1, 2)
42 home('jinhao')
43 # transfer()

有参装饰器模板

def outer(func_name):
    def inner(*args,**kwargs):
        res = func_name(*args, **kwargs)
		  return res
    return inner

def outer_plus(others1,others2):
    def outer(func_name):
        def inner(*args,**kwargs):
            res = func_name(*args, **kwargs)
            return res
        return inner
   	 return outer
@outer_plus(1,2)
def index():
    pass

 。

===========================================================================补充

叠加多个装饰器的加载、运行分析

  分析图

 

posted on 2023-11-22 21:13  认真的六六  阅读(6)  评论(0编辑  收藏  举报