装饰器函数
阅读目录:
-
开闭原则
-
装饰器的概述
-
同一个函数被多个装饰器装饰
-
带参数的装饰器
- 装饰器的应用
开闭原则 设计模式六大原则之一
又被称为开放封闭原则,主要包括以下两点:
1.对功能的扩展开放(即可以加功能)
2.对(源)代码的修改是封闭的(即原来的代码不能修改,为了程序的稳定性)
总结下来就是 不改变源函数(原来的调用方式,函数名,参数,返回值),只改变函数体(仅限于对函数体增加代码,不能更改原有的代码)),就能实现新功能的扩展.
装饰器的概述
装饰器的本质:一个闭包函数
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展(在函数的前、后添加功能)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def wrapper(fn): def inner(): print('开外挂') # 在原有函数前面加新功能 fn() print('关闭外挂') # 在原有函数后面加新功能 return inner def play(): # 对原有函数完全没有修改 print('双击LOL') print('选择狂战士') print('进草丛') print('狮子看') print('崩山击') play = wrapper(play) # 这个需要的时候写上,不需要的时候注释掉 play() # 对原有的调用方式完全没有变
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def wrapper(fn): def inner(*args, **kwargs): # 无敌传参-->形参,聚合 print('开外挂') # 在原有函数前面加新功能 f = fn(*args, **kwargs) # 这里是fn函数的调用(-->实参,打散),所以fn函数的返回值被这里接收 print('关闭外挂') # 在原有函数后面加新功能 return f return inner def play(username, password): # 对原有函数完全没有修改 print('登录:', username, password) print('双击LOL') print('选择狂战士') print('进草丛') print('狮子看') print('崩山击') return '十字斩刀者' def xiaoxiaole(qq): print('登录QQ账号:', qq) print('消消乐') return '奖励10000分' play = wrapper(play) # 这个需要的时候写上,不需要的时候注释掉 ret = play('alex', '123') # 对原有的调用 print(ret) xiaoxiaole = wrapper(xiaoxiaole) re = xiaoxiaole(11010) print(re)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 通用装饰器写法 --> Python里面的动态代理 # # 装饰器存在的意义:在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能(与不使用装饰器之前相比,使用装饰器后,原有目标函数的定义完全不改动,调用完全不改动,执行之后的效果除了增加新的功能也完全不改动) def wrapper(fn): # fn是目标函数 def inner(*args, **kwargs): # 无敌形参-->为了目标函数的传参 '''在执行目标函数之前...''' ret = fn(*args, **kwargs) # 调用目标函数,ret是目标函数的返回值 '''在执行目标函数之后...''' return ret # 把目标函数返回值返回,保证函数正常的结束(不改变目标函数原有的调用方式还能实现想要的功能) return inner @wrapper # 加在目标函数正上方,相当于 target_func = wrapper(target_func) def target_func(): pass # target_func = wrapper(target_func) # 此时target_func就是fn 写了@wrapper就不需要这句话了 target_func() # 此时执行的是inner
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from functools import wraps # 引入函数模块 # 装饰器: 对传递进来的函数进⾏包装. 可以在目标函数之前和之后添加任意的功能. def wrapper(func): @wraps(func) # 加在最内层函数最上方,使用函数原来的名字 def inner(*args, **kwargs): '''在执⾏目标函数之前要执⾏的内容''' ret = func(*args, **kwargs) '''在执⾏目标函数之后要执⾏的内容''' return ret return inner # @wrapper 相当于 target_func = wrapper(target_func) 语法糖 @wrapper # 加在目标函数最上方 def target_func(): print("我是目标函数") # 调⽤⽬标函数 target_func() print(target_func.__name__) # 不再是inner,而是target_func了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def wrapper1(fn): def inner(*args, **kwargs): print("我是wrapper1前") ret = fn(*args, **kwargs) print("我是wrapper1后") return ret return inner def wrapper2(fn): def inner(*args, **kwargs): print("我是wrapper2前") ret = fn(*args, **kwargs) print("我是wrapper2后") return ret return inner def wrapper3(fn): def inner(*args, **kwargs): print("我是wrapper3前") ret = fn(*args, **kwargs) print("我是wrapper3后") return ret return inner # 就近原则 @wrapper1 @wrapper2 @wrapper3 def func(): print("我是可怜的func") func() # 就近原则: 1 2 3 func 3 2 1 >>> 我是wrapper1前 我是wrapper2前 我是wrapper3前 我是可怜的func 我是wrapper3后 我是wrapper2后 我是wrapper1后 解析: 执⾏顺序: 首先@wrapper3装饰起来. 然后获取到一个新函数是wrapper3中的inner. 然后执行@wrapper2.这个时候,wrapper2装饰的就是wrapper3中的inner了...以此类推. 所以. 执⾏顺序就像: 第一层装饰器前(第二层装饰器前(第三层装饰器前(目标)第三层装饰器后)第二层装饰器后)第一层装饰器后. 程序从左到右执⾏起来,就是我们看到的结果.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 函数名+():代表函数的执行 --> 这里返回的结果是一个函数名 # 装饰器:结构是 @+函数名--->接收 函数名+() 的返回结果 一个函数名 # @wrapper_out(True) 执行顺序为先计算函数调用的结果,拿结果作为装饰器 -->等价于后面函数的调用加了一个小括号 @(wrapper_out(True)) 这里之所以传进去实参也是因为这里是函数wrapper_out的调用 def wrapper_out(flag): # 装饰器本身的函数 def wrapper(fn): # 目标函数 def inner(*args, **kwargs): # 无敌形参-->为了目标函数的传参 if flag == True: '''在执行目标函数之前...''' ret = fn(*args, **kwargs) # 调用目标函数,ret是目标函数的返回值 '''在执行目标函数之后...''' return ret # 把目标函数返回值返回,保证函数正常的结束(不改变目标函数原有的调用方式还能实现想要的功能) else: ret = fn(*args, **kwargs) return ret return inner return wrapper # 语法糖 @ @wrapper_out(True) #加在目标函数正上方,相当于 target_func = wrapper(target_func) def target_func(): pass # 解析: 注意: 咱们之前的写法是@wrapper 其中wrapper是一个函数(名). 那么也就是说,如果我能让wrapper这⾥换成个函数(名)就⾏了. wrapper_out(True)返回的结果是wrapper也是一个函数啊. 刚刚好和前面的@组合成一个@wrapper. 依然还是原来那个装饰器. 只不过这⾥里套了3层. 但你要能看懂. 其实还是原来那个装饰器@wrappe. 执⾏步骤: 先执⾏wrapper(True),然后再@返回值,返回值恰好是wrapper,结果就是@wrapper.
装饰器的应用:
''' 员工星系表框架: 要求先登录,之后才能进行增删改查操作.(在增,删,改,查的每个函数上方添加登录验证的装饰器) ''' menu = ("查看", "添加", "修改", "删除", "退出") flag = False # 没登录 def login(): global flag username = input("请输入用户名:") password = input("请输入密码:") if username == "alex" and password == "123": flag = True print("登录") else: flag = False print("用户名密码错误") # 登录验证装饰器 def login_verify(fn): def inner(*args, **kwargs): # 登录校验 while 1: if flag == True: ret = fn(*args, **kwargs) return ret else: print('对不起, 您还没有登录') login() return inner def chakan(): print("==============================查看") @login_verify def tianjia(): print("============================添加") @login_verify def xiugai(): print("=======================修改") @login_verify def shanchu(): print("=========================删除") while 1: for i in range(len(menu)): print(i + 1, menu[i]) num = input("请输入你要执行的菜单:") if num == "1": chakan() elif num == "2": tianjia() elif num == "3": xiugai() elif num == "4": shanchu() elif num == "5": print("程序退出中..........") exit() else: print("输入有误. 请重新选择!")
小结:
1.开闭原则:
对功能的扩展是开放的
对代码的修改是封闭的
2.通用装饰器语法(必须记住):
# 装饰器: 对传递进来的函数进⾏包装. 可以在目标函数之前和之后添加任意的功能. def wrapper(fn): # fn是目标函数. def inner(*args, **kwargs): # 为了目标函数的传参(聚合) '''在执行目标函数之前要执行的内容''' ret = fn(*args, **kwargs) # 调用目标函数(打散), ret是目标函数的返回值 '''在执行目标函数之后要执行的内容''' return ret # 把目标函数返回值返回. 保证函数正常的结束 return inner @wrapper # target_func = wrapper(target_func) def target_func(): pass # 调用目标函数 target_func() # 此时执行的是inner
3.同一个函数被多个装饰器装饰(就近原则)
@wrapper1
@wrapper2
@wrapper3
def func():
pass
执行的结果:1 2 3 func 3 2 1(目标函数被离得近的装饰器括起来)
4.带参数的装饰器
def wrapper_out(参数): # 装饰器本身的参数 def wrapper(fn): # fn是目标函数 def inner(*args, **kwargs): # 目标函数执行需要的参数-->聚合 '''在目标函数之前需要执行的内容''' ret = fn(*arg, **kwargs) # 调用目标函数,ret是目标函数的返回值-->打散 '''在目标函数之后需要执行的内容''' return ret # 把目标函数返回值返回,保证函数以原有的调用方式正常的结束 return inner return wrapper @wrapper_out(实参) # 执行的时候. 先执行函数的调用然后使用返回值和前面的@组合成装饰器语法糖 def func(): # 目标函数的定义 pass func() # 函数的调用