设为首页 加入收藏

TOP

Python多个装饰器的顺序
2019-03-19 14:01:50 】 浏览:43
Tags:Python 多个 装饰 顺序

更新:多个装饰器的调用顺序是自下往上,但是运行时的执行顺序是自上往下!!!


原文链接:http://www.cnblogs.com/nisen/p/6193426.htmlutm_source=itdadao&utm_medium=referral


装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思。

疑问

大部分涉及多个装饰器装饰的函数调用顺序时都会说明它们是自上而下的,比如下面这个例子:

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)

上面代码先定义里两个函数:decotator_a, decotator_b, 这两个函数实现的功能是,接收一个函数作为参数然后返回创建的另一个函数,在这个创建的函数里调用接收的函数(文字比代码绕人)。最后定义的函数f采用上面定义的decotator_a, decotator_b作为装饰函数。在当我们以1为参数调用装饰后的函数f后,decotator_a, decotator_b的顺序是什么呢(这里为了表示函数执行的先后顺序,采用打印输出的方式来查看函数的执行顺序)?

如果不假思索根据自下而上的原则来判断地话,先执行decorator_a再执行decorator_b, 那么会先输出Get in decotator_a,Get in inner_a再输出Get in decotator_b,Get in inner_b。然而事实并非如此。

实际上运行的结果如下:

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里的funcf,最后返回的是f调用的值,所以在最外面看起来就像直接再调用f一样。

疑问的解释

当理清上面两方面概念时,就可以清楚地看清最原始的例子中发生了什么。
当解释器执行下面这段代码时,实际上按照从下到上的顺序已经依次调用了decorator_adecorator_b,这是会输出对应的Get in decorator_aGet in decorator_b。 这时候f已经相当于decorator_b里的inner_b。但因为f并没有被调用,所以inner_b并没有调用,依次类推inner_b内部的inner_a也没有调用,所以Get in inner_aGet in inner_b也不会被输出。

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

然后最后一行当我们对f传入参数1进行调用时,inner_b被调用了,它会先打印Get in inner_b,然后在inner_b内部调用了inner_a所以会再打印Get in inner_a, 然后再inner_a内部调用的原来的f, 并且将结果作为最终的返回。这时候你该知道为什么输出结果会是那样,以及对装饰器执行顺序实际发生了什么有一定了解了吧。

当我们在上面的例子最后一行f的调用去掉,放到repl里演示,也能很自然地看出顺序问题:

  test git:(master)  python
Python 2.7.11 (default, Jan 22 2016, 08:29:18)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import test13
Get in decorator_a
Get in decorator_b
>>> test13.f(1)
Get in inner_b
Get in inner_a
Get in f
2
>>> test13.f(2)
Get in inner_b
Get in inner_a
Get in f
4
>>>

在实际应用的场景中,当我们采用上面的方式写了两个装饰方法比如先验证有没有登录@login_required, 再验证权限够不够时@permision_allowed时,我们采用下面的顺序来装饰函数:

@login_required
@permision_allowed
def f()
  # Do something
  return
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Kafka史上最详细原理总结   .. 下一篇Error while fetching metadata w..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目