7.装饰器
装饰器
在说装饰器之前我们先说一个软件设计原理:开闭原则,又被成为开放封闭原则,你的代码对功能的扩展是开放的,你的程序对修改源码是封闭的,这样的软件设计思路可以更好的维护和开发.
开放:多功能扩展开放
封闭:对修改代码封闭
接下来我们来看一下装饰器,首先我们模拟一下女娲造人
def creat_people(): print("女娲很厉害,捏一个泥人吹一口气就成了人") creat_people()
ok! 很简单. 但是现在问题来了. 上古时期啊. 天气很不稳定. 这时候呢大旱三年. 女娲再去造人啊就很困难了. 因为啥呢? 没水. 也就是说. 女娲想造人必须得先和泥. 浇点儿水才能造人.
def creat_people(): print("浇水") print("女娲很厉害,捏一个泥人吹一口气就成了人") creat_people()
搞定. 但是, 我们来想.是不是违背了我们最开始的那个约定"开闭原则", 我们是添加了 新的功能. 对添加功能开放. 但是修改了源代码啊. 这个就不好了. 因为开闭原则对修改是封闭的. 那怎么办.我们可以这样做
def creat_people(): print("女娲很厉害,捏一个泥人吹一口气就成了人") def warter(): print("先浇水") creat_people() warter()
现在问题又来了. 你这个函数写好了. 但是由于你添加了功能. 重新创建了个函数. 在这之前访问过这个函数的人就必须要修改代码来访问新的函数water() 这也要修改代码. 这个也不好. 依然违背开闭原则. 而且. 如果你这个函数被大量的人访问过. 你让他们所有人都去改. 那你就要倒霉了. 不干死你就见鬼了. 那怎么办才能既不修改原代码, 又能添加新功能呢? 这个时候我们就需要一个装饰器了. 装饰器的作用就是在不修改原有代码的基础上, 给函数扩展功能.
def creat_people(): print("女娲很厉害,捏一个泥人吹一口气就成了人") def warter(fn): def inner(): print("先浇水") fn() print("施肥") return inner creat_people = warter(creat_people) creat_people()
1.先访问warter(create_people)
2.把你的目标函数传递给warter的形参fn,那么后面如果执行了fn意味着执行你的目标函creat_people
3.warter()执行就一句话,返回inner函数,这个时候,程序认为warter()函数执行完,哪么前面的creat_pople函数名被重新覆盖成inner函数
4.执行create_people函数,实际上执行了inner函数,而inner中访问的恰恰使我们最开始传递进取的原生的create_people函数
结论:我们使用warter函数把create_people给包装一下,在不修改creat_people的前提下完成create_people函数的功能添加
这是装饰器的雏形接下来我们观察一下代码,很不要理解,所以我们可以使用一下语法糖来简化我们的代码
def warter(fn): def inner(): print("先浇水") fn() print("施肥") return inner @warter def creat_people(): print("女娲很厉害,捏一个泥人吹一口气就成了人") creat_people() 结果: 先浇水 女娲很厉害,捏一个泥人吹一口气就成了人 施肥
我们发现代码运行的结果是一样的,所谓的语法糖:@装饰器
类似的操作在我们生活中还有很多,比方说,约一约
def wrapper(fn): def inner(): print("问金井老板,行情怎么样") fn() print("金老板骗我,恨你") return inner @wrapper def yue(): print("约一约") yue()
ok, 接下来. 我们来看一下, 我约的话, 我想约个人. 比如约wusir, 这时, 我们要给函数添加 一个参数
def wrapper(fn):
def inner():
print("问金井老板,行情怎么样")
fn()
print("金老板骗我,恨你")
return inner
@wrapper
def yue(name):
print("约一约",name)
yue("wusir")
结果:
Traceback (most recent call last):
File "D:/s18/day15装饰器/__init__.py", line 38, in <module>
yue("wusir")
TypeError: inner() takes 0 positional arguments but 1 was given
程序报错的原因:我们在外访问yue()的时候,实际上是访问inner函数,而inner函数没有参数,我们给了个参数,这肯定报错,怎么办,我们在inner上加参数就好了
def wrapper(fn): def inner(name): print("问金井老板,行情怎么样") fn(name) print("金老板骗我,恨你") return inner @wrapper def yue(name): print("约一约",name) yue("wusir")
这样就够了么? 如果我的yue()改成两个参数呢? 你是不是还要改inner. 对了. 用*args和**kwargs来搞定多个参数的问题
def wrapper(fn): def inner(*args,**kwargs): #聚合 print("问金井老板,行情怎么样") fn(*args,**kwargs) #解散 print("金老板骗我,恨你") return inner @wrapper def yue(name): print("约一约",name) yue("wusir")
搞定. 这时 wrapper()函数就是一个可以处理带参数的函数的装饰器光有参数还不够. 那返回值呢?
def wrapper(fn): def inner(*args,**kwargs): #聚合 print("问金井老板,行情怎么样") ret = fn(*args,**kwargs) #解散 print("金老板骗我,恨你") return ret #把返回值给调用者 return inner @wrapper def yue(name): print("约一约",name) return "小萝莉" r = yue("wusir") #这里接收到的返回值是inner返回,inner的函数值是目标函数的返回值 print(r)
返回值和参数我们搞定了,接下来给出的装饰器的完整模型代码(必须记住)
#装饰器:对传递进来的函数进行包装,可以在目标函数之前和之后添加任意功能 def wrapper(func): def inner(*args,**kwargs): '''执行目标函数之前的代码''' ret = func(*args,**kwargs) """执行目标函数之后的代码""" return ret return inner @wrapper def func(): print("我是目标函数") func() #调用目标函数
我们虽然访问的是func函数. 但是实际上执行的是inner函数. 这样就会给下游的程序员带来困惑. 之前不是一直执行的是func么. 为什么突然换成了inner. inner是个什么鬼? 为了不让下游程序员有这样的困惑. 我们需要把函数名修改一下. 具体修改方案:
from functools import wraps def wrapper(func): @wraps(func) def inner(*args,**kwargs): '''执行目标函数之前的代码''' ret = func(*args,**kwargs) """执行目标函数之后的代码""" return ret return inner #@wrapper 相当于 func = wrapper(func) @wrapper def func(): print("我是目标函数") func() print(func.__name__) #不在是inner,而是func了 @wrapper def new_func(): print("我是另外一个目标函数") new_func() print(new_func.__name__) #new_func
装饰器传参
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(): print("走起,约去") yue()
多个装饰器修饰一个函数
def wapper1(fn): def inner(*agrs,**kwargs): print("111") ret = fn(*agrs,**kwargs) print("222") return ret return inner def wapper2(fn): def inner(*agrs,**kwargs): print("333") ret = fn(*agrs,**kwargs) print("444") return ret return inner def wapper3(fn): def inner(*agrs,**kwargs): print("555") ret = fn(*agrs,**kwargs) print("666") return ret return inner @wapper3 @wapper2 @wapper1 def eat(): #就近原则 3 2 1 eat 1 2 3 print("我想吃水果") eat() 结果: 555 333 111 我想吃水果 222 444 666