装饰器执行顺序
考虑如下代码执行顺序:
def decorator_a(func):
print('Get in decorator_a')
def inner_a(*args, **kwargs):
print('Get in inner_a')
return func(*args, **kwargs)
return inner_a
def decorator_b(func):
print('Get in decorator_b')
def inner_b(*args, **kwargs):
print('Get in inner_b')
return func(*args, **kwargs)
return inner_b
@decorator_a
@decorator_b
def f(x):
print('Get in f')
return x * 2
f(1)
结果为:
Get in decorator_b
Get in decorator_a
Get in inner_a
Get in inner_b
Get in f
我们知道,用语法糖的装饰器我们可以将被装饰的函数转换为
def f(x):
print('Get in f')
return x * 2
f = decorator_a(decorator_b(f)) # decorator 定义省略
# 调用 f 函数
f(1)
因此,当执行到 f = decorator_a(decorator_b(f)) 时,先执行了 decorator_b(f) ,输出 Get in decorator_b 并返回 inner_b 函数,然后传递给 decorator_a 作为参数,即 decorator_a(inner_b) ,输出 Get in decorator_a 并返回 inner_a 函数。
当执行到 f(1) 调用 f 函数时,相当于执行了 decorator_a(decorator_b(f))(1) 。由前面得知 decorator_a(decorator_b(f)) 返回了 inner_a 函数,所以 decorator_a(decorator_b(f))(1) 等价于 inner_a(1) ,于是进入到 inner_a 函数输出 Get in inner_a 并执行函数内的 func(*args, **kwargs),这里 func 即为 decorator_a 的参数:decorator_b(f) ,即 func(*args, **kwargs) -> decorator_b(f)(1) -> inner_b(1) ,所以类似于前面同样的逻辑,进入到 inner_b 函数输出 Get in inner_b 并执行 func(*args, **kwargs),这里 func 即为 f 函数,所以最后进入 f 函数,输出 Get in f 。
前面的逻辑看起来有点绕,我们也可以用栈的思想来理解:在定义装饰器函数的时候,相当于函数入栈,上面的例子就是 f -> decorator_b -> decorator_a;在被调用的地方,相当于函数出栈 decorator_a -> decorator_b -> f 。
由上面的例子,我们再来思考一下三层嵌套的装饰器(即包含装饰器本身的参数)的输出情况:
def decorator_a(n):
print(f'Get in decorator_a, n: {n}')
def middle_a(func):
print('Get in middle_a')
def inner_a(*args, **kwargs):
print('Get in inner_a')
return func(*args, **kwargs)
return inner_a
return middle_a
def decorator_b(n):
print(f'Get in decorator_b, n: {n}')
def middle_b(func):
print('Get in middle_b')
def inner_b(*args, **kwargs):
print('Get in inner_b')
return func(*args, **kwargs)
return inner_b
return middle_b
@decorator_a('a')
@decorator_b('b')
def f(x):
print('Get in f')
return x * 2
f(1)
结果为:
Get in decorator_a, n: a
Get in decorator_b, n: b
Get in middle_b
Get in middle_a
Get in inner_a
Get in inner_b
Get in f
对于三层嵌套结构,我们同样可以将语法糖形式转换为函数形式。上面的例子可以写为:
def f(x):
print('Get in f')
return x * 2
f = decorator_a(n)(decorator_b(n)(f)) # decorator 定义省略
# 调用 f 函数
f(1)
与两层嵌套不同的是,三层嵌套在包装时已经调用了最外层,并返回内层函数(先剥开了最外层,并返回剩下的内容,剩下的内容不难发现与二层装饰器相同)。如果我们只看一个装饰器的话,即 f = decorator_a(n)(f) 所以会先调用装饰器,进入装饰器最外层并输出 Get in decorator_a, n: a ,然后返回里面两层结构(后面的分析就与前一个例一样了)