Python装饰器(Decorator)简介

Python有许多出色的语言特性,装饰器(Decorator)便是其中一朵奇葩。先来看看一段代码:

def deco1(f):
    print 'decorate 1'
    return f

def deco2(f):
    print 'decorate 2'
    return f

@deco1
@deco2
def foo(): return 'hello'

保存并执行上面的代码,你会看到如下输出:

decorate 2
decorate 1

函数foo没有被调用,但是deco1,deco2被按照一个顺序被调用了。deco1和deco2就是装饰器。从上面的输出可以看出两点:

1 装饰器是在代码装载时被调用的

2 调用的顺是从下到上

现在来说说装配器到底做了什么。看代码:

def deco1(f):
    print 'decorate 1',f
    return f

def deco2(f):
    print 'decorate 2',f
    return f

@deco1
@deco2
def foo():
    return 'hello'

这段代码的执行结果为:

decorate 2 <function foo at 0x00000000021F79E8>
decorate 1 <function foo at 0x00000000021F79E8>

装饰器函数的参数‘f’被打印出来,而且就是我们的函数‘foo’,可见装饰器的参数就是被装饰的函数。真的是这样吗?再来看代码:

def wraper(n,f):
    def foo1():
        return '%s(%s)'%(n,f())return foo1
    

def deco1(f):
    print 'decorate 1',f
    return wraper('decorate 1',f)

def deco2(f):
    print 'decorate 2',f
    return wraper('decorate 2',f)

@deco1
@deco2
def foo():
    return 'hello'

这段代码的执行结果为:

decorate 2 <function foo at 0x0000000002147A58>
decorate 1 <function foo1 at 0x0000000002147AC8>

我觉得我现在可以总结一下了:

1 装饰器在代码装载时被调用;

2 调用顺序是从下到上的;

3 被装饰函数‘foo’作为参数传递给第一个装饰器‘deco2’,返回值将作为参数传递给第二个装饰器‘deco1’,然后依次向上直到最顶端的装饰器;

4 最顶端的装饰器的返回值就是被装饰以后的函数,我们暂时称之为,也就是我们将来要执行的那个‘foo’。

 下面,来我们说说装饰后的函数,被调用会有什么结果,看代码:

 

def wraper(n,f):
    def foo1():
        return '%s(%s)'%(n,f())
    return foo1

def deco1(f):
    print 'decorate 1',f
    return wraper('decorate 1',f)

def deco2(f):
    print 'decorate 2',f
    return wraper('decorate 2',f)

@deco1
@deco2
def foo():
    return 'hello'


print foo()

这段代码的执行结果为:

decorate 2 <function foo at 0x0000000002127A58>
decorate 1 <function foo1 at 0x0000000002127AC8>
decorate 1(decorate 2(hello))

不难看出,调用顺序与装饰顺序刚好相反,最终函数也就是最顶端的装饰器返回的函数最先被调用,然后依次调用到最初那个函数‘foo’。我在这里故意规避了一个基本的事实,就是:其实最后被调用的就是顶端装饰返回的那个函数,你必须手动在这个函数中调用前面的函数,见这里:

return '%s(%s)'%(n,f())

,才会产生调用连的效果。所以装饰器并不会帮你完成所有的事情,他给了你充分的自由。说的这里,你们要问了(如果你有足够的好奇心):‘装饰器能带参数吗’。答案是能,见下面的代码:

 

def wraper(n,f):
    def foo1():
        return '%s(%s)'%(n,f())
    return foo1

def deco(n):
    def deco1(f):
        print n,f
        return wraper(n,f)
    return deco1

@deco('decorate 1')
@deco('decorate 2')
def foo():
    return 'hello'


print foo()

这段代码的执行结果为:

decorate 2 <function foo at 0x0000000002117AC8>
decorate 1 <function foo1 at 0x0000000002117B38>
decorate 1(decorate 2(hello))

哇,和之前的结果一模一样,代码还被简化了不少。这是怎么回事呢,我来简单解释下,函数‘deco’不是装饰器,他只是一个返回装饰器的函数,当你把它放到装饰符号‘@‘后面时,python的语法起了一个美妙的作用,他会先调用这个函数,然后用返回值值作为装饰器。

大概就是这样啦,语法大师也许会描述的更详细更专业。源码狂人还可能深入到python的C代码里寻找成因。不过,作为一个有理智的好青年我们就点到为止吧。

下次,我们再来掰扯一下,装饰器都可以变出哪些戏法吧。洗洗睡了。

 

posted on 2015-04-24 21:47  索夫特威尔  阅读(764)  评论(0编辑  收藏  举报

导航