关于多个装饰器装饰一个函数执行顺序的问题
我们通过两个实例来解开我们的疑惑:
实例一:
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函数的输出与函数的执行的一个先后顺序。