装饰器执行的原理/顺序
装饰器的顺序
多个装饰器装饰函数时,离函数近的装饰器先进行装饰,下面的装饰器先装饰完后将整个装饰结果给第一个装饰器进行装饰
即:封装时自内而外(自下而上),执行时自外而内(自上而下)
装饰器的执行原理
以代码为例:
##定义装饰器
def check(func):
print("验证权限")
def inner(identify):
func(identify)
return inner
###装饰装饰器
@check
def f1(user_id):
if user_id=="root":
print("允许root用户操作")
else:
print(f"不允许{user_id}用户操作")
f1("root")
流程如下:
装饰器函数:
这里的装饰器函数是check,check函数接收一个参数func,其实就是接收一个函数的名字,check函数内部又定义了一个函数inner,在inner函数中增加权限校验,并在验证完权限后调用传进来的参数func,同时check的返回值为内部函数inner,其实就是一个闭包函数。
被装饰器装饰的函数:
在函数f1上增加@check,其实就是给f1函数额外的增加了权限验证的输出语句。当python解释器执行到这条语句的时候,会去调用check函数,同时将被装饰的函数名作为参数传入(此时为f1),根据闭包分析,在执行check函数的时候,返回值是inner函数,同时把返回值inner赋值给f1函数(这里其实传递的是函数的引用),此时的f1已经不是未加装饰时的f1了,即f1函数已经改变了,不再是原来的内容了,而是指向了check.inner函数的地址。
调用被装饰器装饰函数的过程:
在调用f1(“root”)的时候,其实调用的是check.inner函数,那么此时就会先执行权限验证,然后再调用原来的f1(“root”),该处的f1就是通过装饰传进来的参数f1。
从而,就完成了对函数f1的装饰,实现了权限验证功能。
多个装饰器装饰函数时的执行过程
#####定义第一个装饰器
def first(fun):
print('----第一个装饰器封装----')
def inner1(*args,**kwargs):
print('----第一个装饰器执行----')
return '<第一个>' + fun(*args,**kwargs) + '</第一个>'
return inner1
#####定义第二个装饰器
def second(fun):
print('----第二个装饰器封装----')
def inner2(*args,**kwargs):
print('----第二个装饰器执行----')
return '<第二个>' + fun(*args,**kwargs) + '</第二个>'
return inner2
#####装饰器去装饰函数
@first
@second
def test(a,b):
print('----被装饰的函数开始执行----')
return f'hello python decorator,{a}+{b}={a+b}'
print("开始调用函数test")
ret = test(1,3)
print(ret)
运行结果为:
可以看到,多个装饰器去装饰同一个函数的时候,最里面的装饰器是最开始进行装饰的。
执行步骤:
第一步:输出语句----第二个装饰器封装----
用@second装饰器去对函数test进行装饰,因此会先去执行second函数,因为此时还没有调用test函数,所以不会执行second函数里面的inner2函数,所以就输出了“----第二个装饰器封装----“语句,并且返回inner2的引用,所以此时就是second(test)函数。
第二步:输出语句----第一个装饰器封装----
该函数又被@first函数装饰,因此就先去执行first函数,因为此时还没有调用test函数,所以不会执行first函数里面的inner1函数,所以就输出了“----第一个装饰器封装----“语句,并且返回inner1的引用,所以此时就是first(second(test))函数。
所以前1,2步就是进行函数的封装。
第三步:输出语句开始调用函数test
函数装饰完毕之后,就执行到print(“开始调用函数test”)语句了,输出“开始调用函数test”
第四步:输出语句----第一个装饰器执行----
调用函数test(1,3),此时的函数不再是原来的test了,而是经过装饰器装饰的函数了,此时的函数是first(second(test))。因为返回的是inner1函数的引用,所以会先去执行inner1函数,输出“第一个装饰器执行“,然后返回<第一个>+inner2的引用+</第一个>。
第五步:输出语句----第二个装饰器执行----
接着去执行inner2的引用,所以就会输出语句“第二个装饰器执行”,并且返回<第二个>+test的引用+</第二个>。
第六步:输出语句----被装饰的函数开始执行----
接着去执行test的引用,此时就真正的调用函数test了,所以会输出语句“----被装饰的函数开始执行----”
第七步:输出语句<第一个><第二个>hello python decorator,1+3=4</第二个></第一个>
然后返回hello python decorator,1+3=4,再将前面返回的内容一层层组合,所以最终的返回值是<第一个><第二个>hello python decorator,1+3=4</第二个></第一个>