关于多个装饰器装饰一个函数执行顺序的问题

Posted 道长

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于多个装饰器装饰一个函数执行顺序的问题相关的知识,希望对你有一定的参考价值。

我们通过两个实例来解开我们的疑惑:

实例一:

def war1(func):
    def inner(*args, **kwargs):
        print("======war1 start=====")
        func(*args, **kwargs)
        print("======war1 end=====")
    return inner
def war2(func):
    def inner(*args,**kwargs):
        print("======war2 start=====")
        func(*args,**kwargs)
        print("======war2 end=====")
    return inner
@war1
@war2
def f():
    print("****self****")
f()

实际输出结果为:

======war1 start=====
======war2 start=====
****self****
======war2 end=====
======war1 end=====

好吧这个结果似乎有点出乎我们的意料:不是说好的谁近谁先来的嘛(我裤子都脱了,结果。。。)

我们自己臆想的结果:

======war2 start=====
======war2 end=====
======war1 start=====
======war1 end=====
****self****

那么首先我们知道我们为一个函数装饰其实就相当于:

f = war1(f)或者
f = war2(f)                ####这个时候f=inner,而不再是原来的函数。只是用户不知道而已。

那么问题来了:

当走到这一步的时候说明,两个inner函数已经执行了。

也就是他们发生在我们真正的调用之前,那么真正调用的时候才是我们按照我们的就近原则,也就是限制性war2中的代码,在执行war1中的代码。

 

 

 实例二:

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_b
@decorator_a
def f(x):
    print( \'Get in f\')
    return x * 2

f(1)

实际执行结果为:

执行结果
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f

函数和函数调用的区别

为什么是先执行 inner_b 再执行 inner_a 呢?为了彻底看清上面的问题,得先分清两个概念:函数和函数调用。上面的例子中 f 称之为函数, f(1) 称之为函数调用,后者是对前者传入参数进行求值的结果。在Python中函数也是一个对象,所以 f 是指代一个函数对象,它的值是函数本身, f(1) 是对函数的调用,它的值是调用的结果,这里的定义下 f(1) 的值2。同样地,拿上面的 decorator_a 函数来说,它返回的是个函数对象 inner_a ,这个函数对象是它内部定义的。在 inner_a里调用了函数 func ,将 func 的调用结果作为值返回。

装饰器函数在被装饰函数定义好后立即执行

其次得理清的一个问题是,当装饰器装饰一个函数时,究竟发生了什么。现在简化我们的例子,假设是下面这样的:

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

@decorator_a
def f(x):
    print\'Get in f\')
    return x * 2

 

正如很多介绍装饰器的文章里所说:

@decorator_a
def f(x):
    print (Get in f\')
    return x * 2

# 相当于
def f(x):
    print\'Get in f\')
    return x * 2

f = decorator_a(f)

 

所以,当解释器执行这段代码时, decorator_a 已经调用了,它以函数 f 作为参数, 返回它内部生成的一个函数,所以此后 f 指代的是 decorater_a 里面返回的 inner_a 。所以当以后调用 f 时,实际上相当于调用 inner_a ,传给 f 的参数会传给 inner_a , 在调用 inner_a 时会把接收到的参数传给 inner_a 里的 func 即 f ,最后返回的是 f 调用的值,所以在最外面看起来就像直接再调用 f 一样。


那么对于实例一与实例二,还有一点不同的,哈哈哈,那就是被装饰函数的执行顺序,在两个实例中是不一样的,这是为什么呢?

这个就跟我们的装饰器没什么关系了:
主要是因为inner函数的输出与函数的执行的一个先后顺序。

 

 

 

以上是关于关于多个装饰器装饰一个函数执行顺序的问题的主要内容,如果未能解决你的问题,请参考以下文章

Python装饰器执行顺序详解

多个装饰器修饰一个函数时的调用顺序

装饰器实现flask登录,没登录不让看index页面,多个装饰器装饰一个函数的执行顺序

Python多个装饰器的顺序 转载

装饰器的补充(叠加多个装饰器,有参装饰器,三元表达式,生成式,匿名函数)

python多个装饰器的执行顺序