装饰器
在说装饰器之前啊. 我们先说⼀个软件设计的原则: 开闭原则, 又被成为开放封闭原则, 你的代码对功能的扩展是开放的, 你的程序对修改源代码是封闭的. 这样的软件设计思路路可以 更好的维护和开发.
开放: 对功能扩展开放
封闭: 对修改代码封闭
这里定义一个女娲造人的函数
def zaoren(): # print("浇水") # 此需求有的时候需要. 有的时候不需要 print("捏个泥人") print("吹口仙气") print("你就出来了")
女娲要造人,直接执行函数就行了
假设没有水怎么办? ----->>浇水
def water():
print("浇水")
zaoren()
# 此时的设计就不符合开闭原则
所以在不改变源代码的前提下,我们可以这样做
def wrapper(fn): # fn接收的是一个函数
def inner():
print("浇水")
fn() # 调用你传递进来的函数
print("睡一觉")
return inner
def zaoren():
print("捏个泥人")
print("吹口仙气")
print("你就出来了")
zaoren = wrapper(zaoren)
zaoren()
zaoren()
这里我们就用另外一个装饰器装饰了函数zaoren
这就是装饰器的作用
现在问题⼜来了. 你这个函数写好了. 但是由于你添加了功能. 重新创建了个函数. 在这之前访问过这个函数的人就必须要修改代码来访问新的函数water() 这也要修改代码. 这个也不 好. 依然违背开闭原则. ⽽且. 如果你这个函数被⼤量的人访问过. 你让他们所有⼈都去改.那你就要倒霉了. 不干死你就⻅鬼了.
那怎么办才能既不修改原代码, 又能添加新功能呢? 这个时候我们就需要⼀个装饰器了. 装饰器的作用就是在不修改原有代码的基础上, 给函数扩展功能.
def play(username, password):
print("双击lol")
print("登录", username, password)
print("选择狂战士")
print("进草丛")
print("崩山击, 十字斩")
return "月之光芒"
#
def xiaoxiaole(qq):
print("登录qq账号")
print("消消乐")
#
# # 开挂
# # 关闭外挂
# # 在目标函数前和后插入一段新的代码. 不改变原来的代码
def wrapper(fn): # fn = play
def inner(*args, **kwargs): # 无敌传参 接受到的是元组 ("alex", 123)
print("开挂")
ret = fn(*args, **kwargs) # 接受到的所有参数. 打散传递给正常的参数
print("关闭外挂")
return "月之光芒"
return inner
#
play = wrapper(play) # play = inner
# ret = play('alex',"123")
# print(ret) # None
ret = play(111,222) #这里的参数无论是几个,都能够顺利的传入,就是运用到的无敌传参
print(ret)
下面我们总结一下装饰器的通用写法:
装饰器就是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(): ##这里虽然执行的是target_func实际上还是执行的wrapper
pass
# target_func = wrapper(target_func) # 此时fn就是target_func
target_func() # 此时执行的是inner
这里有个语法糖的运用
@wrapper 就是 target_func = wrapper(target_func)
装饰器传参
from functools import wraps def wrapper(fn): @wraps(fn) def inner(*args, **kwargs): print("金⽼板啊, 行情怎么样.") ret = fn(*args, **kwargs) print("⾦老板骗我") return ret
return inner @wrapper def yue(): print("约⼀一次⼜又不不会死") yue() 那么现在如果查的很严. 怎么办呢? 打电话问⾦老板严不严.
那如果整体⻛声都不是那么 紧呢. 是不是就不需要问⾦老板了.
所以. 我们需要⼀个开关来控制是否要询问金老板.
这时 我们就需要给装饰器传递⼀一个参数.
来通知装饰器要⽤怎么样的⽅式来装饰你的目标函
这里外面又套了一层函数,用来判断是否要用装饰器装饰函数
from functools import wraps
def wrapper_out(flag):
def wrapper(fn):
@wraps(fn)
def inner(*args, **kwargs):
if flag == True: # 查的严啊. 先问问吧
print("问问⾦老板啊, ⾏情怎么样.")
ret = fn(*args, **kwargs)
print("⺾艹, ⾦⽼板骗我")
return ret
else: # 查的不不严. 你慌什什么
ret = fn(*args, **kwargs)
return ret
return inner
return wrapper
@wrapper_out(False)
### 传递True和False来控制装饰器器内部的运⾏行行效果
def yue():
print("约一次⼜不会死")
yue()
注意: 咱们之前的写法是@wrapper 其中wrapper是一个函数. 那么也就是说. 如果我能让 wrapper这里换成个函数就行了. wrapper(True)返回的结果是wrapper也是一个函数啊. 刚刚 好和前⾯面的@组合成一个@wrapper. 依然还是原来那个装饰器. 只不过这里套了3层. 但你要 能看懂. 其实还是原来那个装饰器@wrapper 执行步骤: 先执行wrapper(True) 然后再@返回值. 返回值恰好是wrapper. 结果就是 @wrapper
在这里,我写成这样,更容易看懂一些
def wrapper_out(flag): # 装饰器本身的参数
def wrapper(fn): # 目标函数
def inner(*args, **kwargs): # 目标函数执行需要的参数
if flag == True:
print("问问金老板. 行情怎么样啊")
ret = fn(*args, **kwargs) # 在执行目标函数之前
print("金老板骗我. 恨你")
return ret
else:
ret = fn(*args, **kwargs) # 在执行目标函数之前
return ret
return inner
return wrapper
# 语法糖 @装饰器
@wrapper_out(True) # 先执行wrapper_out(True) 返回一个装饰器 再和@拼接 @装饰器
def yue(): # 被 wrapper装饰
print("走啊. 约不?")
yue()
假使多个装饰器对同一个函数进行装饰,会是什么效果##就近原则##
def wrapper1(fn):
def inner(*args, **kwargs):
print("1111111")
ret = fn(*args, **kwargs)
print("2222222")
return ret
return inner
def wrapper2(fn):
def inner(*args, **kwargs):
print("3333333")
ret = fn(*args, **kwargs)
print("44444444")
return ret
return inner
def wrapper3(fn):
def inner(*args, **kwargs):
print("555555")
ret = fn(*args, **kwargs)
print("666666")
return ret
return inner
# 就近原则
@wrapper1
@wrapper2
@wrapper3
def func():
print("我是可怜的func")
func()
# 1 2 3 func 3 2 1
执行顺序: ⾸首先@wrapper1装饰起来. 然后获取到一个新函数是wrapper1中的inner.
然后执 ⾏@wrapper2.这个时候. wrapper2装饰的就是wrapper1中的inner了了.
所以. 执行顺序就像: 第⼆层装饰器前(第⼀层装饰器前(目标)第一层装饰器后)第二层装饰器后.
程序从左到右执⾏起来. 就是我们看到的结果
###
1111111
3333333
555555
我是可怜的func
666666
44444444
2222222
###