Python有许多出色的语言特性,装饰器(Decorator)即是其中一朵奇葩。先来看看一段代码:python
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就是装饰器。从上面的输出能够看出两点:spa
1 装饰器是在代码装载时被调用的code
2 调用的顺是从下到上blog
如今来讲说装配器到底作了什么。看代码:源码
def deco1(f): print 'decorate 1',f return f def deco2(f): print 'decorate 2',f return f @deco1 @deco2 def foo(): return 'hello'
这段代码的执行结果为:io
decorate 2 <function foo at 0x00000000021F79E8>
decorate 1 <function foo at 0x00000000021F79E8>
装饰器函数的参数‘f’被打印出来,并且就是咱们的函数‘foo’,可见装饰器的参数就是被装饰的函数。真的是这样吗?再来看代码:function
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'
这段代码的执行结果为:class
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代码里寻找成因。不过,做为一个有理智的好青年咱们就点到为止吧。
下次,咱们再来掰扯一下,装饰器均可以变出哪些戏法吧。洗洗睡了。