详解Python 装饰器执行顺序迷思
探究多个装饰器执行顺序
装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思。
疑问
大部分涉及多个装饰器装饰的函数调用顺序时都会说明它们是自上而下的,比如下面这个例子:
defdecorator_a(func): print'Getindecorator_a' definner_a(*args,**kwargs): print'Getininner_a' returnfunc(*args,**kwargs) returninner_a defdecorator_b(func): print'Getindecorator_b' definner_b(*args,**kwargs): print'Getininner_b' returnfunc(*args,**kwargs) returninner_b @decorator_b @decorator_a deff(x): print'Getinf' returnx*2 f(1)
上面代码先定义里两个函数:decotator_a,decotator_b,这两个函数实现的功能是,接收一个函数作为参数然后返回创建的另一个函数,在这个创建的函数里调用接收的函数(文字比代码绕人)。最后定义的函数f采用上面定义的decotator_a,decotator_b作为装饰函数。在当我们以1为参数调用装饰后的函数f后,decotator_a,decotator_b的顺序是什么呢(这里为了表示函数执行的先后顺序,采用打印输出的方式来查看函数的执行顺序)?
如果不假思索根据自下而上的原则来判断地话,先执行decorator_a再执行decorator_b,那么会先输出Getindecotator_a,Getininner_a再输出Getindecotator_b,Getininner_b。然而事实并非如此。
实际上运行的结果如下:
Getindecorator_a
Getindecorator_b
Getininner_b
Getininner_a
Getinf
函数和函数调用的区别
为什么是先执行inner_b再执行inner_a呢?为了彻底看清上面的问题,得先分清两个概念:函数和函数调用。上面的例子中f称之为函数,f(1)称之为函数调用,后者是对前者传入参数进行求值的结果。在Python中函数也是一个对象,所以f是指代一个函数对象,它的值是函数本身,f(1)是对函数的调用,它的值是调用的结果,这里的定义下f(1)的值2。同样地,拿上面的decorator_a函数来说,它返回的是个函数对象inner_a,这个函数对象是它内部定义的。在inner_a里调用了函数func,将func的调用结果作为值返回。
装饰器函数在被装饰函数定义好后立即执行
其次得理清的一个问题是,当装饰器装饰一个函数时,究竟发生了什么。现在简化我们的例子,假设是下面这样的:
defdecorator_a(func): print'Getindecorator_a' definner_a(*args,**kwargs): print'Getininner_a' returnfunc(*args,**kwargs) returninner_a @decorator_a deff(x): print'Getinf' returnx*2
正如很多介绍装饰器的文章里所说:
@decorator_a deff(x): print'Getinf' returnx*2 #相当于 deff(x): print'Getinf' returnx*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一样。
疑问的解释
当理清上面两方面概念时,就可以清楚地看清最原始的例子中发生了什么。
当解释器执行下面这段代码时,实际上按照从下到上的顺序已经依次调用了decorator_a和decorator_b,这是会输出对应的Getindecorator_a和Getindecorator_b。这时候f已经相当于decorator_b里的inner_b。但因为f并没有被调用,所以inner_b并没有调用,依次类推inner_b内部的inner_a也没有调用,所以Getininner_a和Getininner_b也不会被输出。
@decorator_b @decorator_a deff(x): print'Getinf' returnx*2
然后最后一行当我们对f传入参数1进行调用时,inner_b被调用了,它会先打印Getininner_b,然后在inner_b内部调用了inner_a所以会再打印Getininner_a,然后再inner_a内部调用的原来的f,并且将结果作为最终的返回。这时候你该知道为什么输出结果会是那样,以及对装饰器执行顺序实际发生了什么有一定了解了吧。
当我们在上面的例子最后一行f的调用去掉,放到repl里演示,也能很自然地看出顺序问题:
➜testgit:(master)✗python Python2.7.11(default,Jan222016,08:29:18) [GCC4.2.1CompatibleAppleLLVM7.0.2(clang-700.1.81)]ondarwin Type"help","copyright","credits"or"license"formoreinformation. >>>importtest13 Getindecorator_a Getindecorator_b >>>test13.f(1) Getininner_b Getininner_a Getinf 2 >>>test13.f(2) Getininner_b Getininner_a Getinf 4 >>>
在实际应用的场景中,当我们采用上面的方式写了两个装饰方法比如先验证有没有登录@login_required,再验证权限够不够时@permision_allowed时,我们采用下面的顺序来装饰函数:
@login_required @permision_allowed deff() #Dosomething return
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。