Python函数之装饰器
装饰器
一.装饰器的意义
比如:以下函数是供我们调用的,但是我们需要在每个函数中都添加同一个功能,该如何做到呢?
对于函数开放封闭原则,我们一般不允许修改。所以我们引入了装饰器:
def outer(func): def inner(): print("添加在原函数之前") r = func() #保留原函数的返回值 print('添加在原函数之后') return r #将保留的返回值返回回去 return inner @outer #等价于f1 = outer(f1) def f1(): print("F1") def f2(): print("F2") def f3(): print("F3") def f4(): print("F4") f1() #执行函数,f1()已经等价于inner()了
二.简单装饰器的流程剖析
三.带参数的装饰器
3.1
#一般装饰器,不能装饰有参数的函数 def outer(func): #func为待装饰的函数 def inner(): #从上往下执行,遇到def inner,就将函数扔进内存空间,但不执行 print("执行原函数前添加的部分") r = func() #接收原函数的返回值 print("执行原函数后添加的部分") return r #返回原函数的返回值 return inner #返回inner给index @outer #index = outer(index) def index(): print("非常复杂的功能") return True index()
3.2:修改方案:根据参数的个数,我们主要得修改装饰器的内层函数
#如果待包装函数有参数 #需要如何修改装饰器呢 def outer(func): def inner(a1, a2): print("执行原函数前添加的部分") r = func(a1, a2) print("执行原函数后添加的部分") return r return inner @outer def index(a1, a2): print("非常复杂的功能") return a1 + a2 @outer def home(s1, s2): print("非常复杂的功能") return "HOME" ret1 = index(1, 2) #应用了装饰器之后index变成装饰器的内层函数了 ret2 = home(1, 2)
3.3:但是,如果不同的待装饰函数的参数个数不一致该怎么办呢?
修改方案:使用动态参数,*arg和**kwargs扩大接收参数的形式。
def outer(func): def inner(*arg ,**kwargs): print("执行原函数前添加的部分") r = func(*arg ,**kwargs) #内部让*argj元组或**kwargs字典吐出参数,传入原函数 print("执行原函数后添加的部分") return r return inner @outer def home(s1): #一个参数 print("非常复杂的功能") return "HOME" @outer def index(a1, a2): #两个个参数 print("非常复杂的功能") return "INDEX" @outer def show(x1, x2, x3):#三个参数 print("非常复杂的功能") return "SHOW" ret1 = home(1) ret2 = index(1, 2) ret3 = show(1, 2, 3)
四.使用多装饰器装饰一个函数
应用场景:例如有100个函数,其中10个函数需要加上一部分功能,
而另外90个函数还需要加上额外得一部分功能,怎么做?
方法一:
def outer0(func): def inner(*arg ,**kwargs): print("执行原函数前添加的部分") r = func(*arg ,**kwargs) print("执行原函数后添加的部分") return r return inner def outer1(func): def inner(*arg ,**kwargs): print("执行原函数前添加的部分(额外)") print("执行原函数前添加的部分") r = func(*arg ,**kwargs) print("执行原函数后添加的部分") return r return inner @outer0 #功能1 def home(s1): print("非常复杂的功能") return "HOME" @outer0 #功能1 def index(a1, a2): print("非常复杂的功能") return "INDEX" @outer1 #功能2 def show(x1, x2, x3): print("非常复杂的功能") return "SHOW" ret1 = home(1) ret2 = index(1, 2) ret3 = show(1, 2, 3)
缺点:装饰器outer2的代码与outer的代码有重复的部分,我们应该
尽量避免重复代码
方法二:改进版
def outer1(func): def inner(*arg ,**kwargs): print("执行原函数前添加的部分(1)") r = func(*arg ,**kwargs) print("执行原函数前添加的部分(4)") return r return inner def outer0(func): def inner(*arg ,**kwargs): print("执行原函数前添加的部分(2)") r = func(*arg ,**kwargs) print("执行原函数后添加的部分(3)") return r return inner @outer0 def home(s1): print("非常复杂的功能") return "HOME" @outer1 #先执行这个函数的装饰功能 @outer0 #再执行这个函数的装饰功能 def index(a1, a2): print("非常复杂的功能") return "INDEX" ret1 = home(1) ret2 = index(1, 2)
多重装饰器流程分析:(原函数的两次变化)
没应用装饰器之前,原函数就是原函数
应用第一个装饰器(outer0))之后,原函数变成了outer0函数的内层函数了,由func保留了原函数
应用第二个装饰器(outer1)之后,outer0的内层函数也就是此时的原函数传入了第二个装饰器里面
去了,替换成第二个装饰器的内存函数,并有第二个装饰器的参数func保存了原函数。
装饰过程:
执行过程:
装饰次序:
#多层装饰器的流程
#第一个装饰器分别装饰第一层和最后一层
#第二个装饰器分别装饰第二层和倒数第二层
#第三个装饰器分别装饰第三层和倒数第三层
#以此类推,中间放要装饰的函数
五.装饰器的应用
单层装饰器:比如京东的网页,在进入我的购物车之前需要登录,这个登录就是一个装饰器
多层装饰器:对于京东的用户来说,有大家公共的页面,也有对于钻石vip和白金vip不同的
页面,所以在使用第一层登录装饰器之后,还需要加上额外的用户权限的装饰器