Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西。虽然好像,他们要干的事都很相似——都是想要对一个已有的模块做一些“修饰工作”,所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去。但是OO的Decorator简直就是一场恶梦,不信你就去看看wikipedia上的词条(Decorator Pattern)里的UML图和那些代码,这就是我在《 从面向对象的设计模式看软件设计》“餐后甜点”一节中说的,OO鼓励了——“厚重地胶合和复杂层次”,也是《 如此理解面向对象编程》中所说的“OO的狂热者们非常害怕处理数据”,Decorator Pattern搞出来的代码简直就是OO的反面教程。
Python 的 Decorator在使用上和Java/C#的Annotation很相似,就是在方法名前面加一个@XXX注解来为这个方法装饰一些东西。但是,Java/C#的Annotation也很让人望而却步,太TMD的复杂了,你要玩它,你需要了解一堆Annotation的类库文档,让人感觉就是在学另外一门语言。
而Python使用了一种相对于Decorator Pattern和Annotation来说非常优雅的方法,这种方法不需要你去掌握什么复杂的OO模型或是Annotation的各种类库规定,完全就是语言层面的玩法:一种函数式编程的技巧。如果你看过本站的《函数式编程》,你一定会为函数式编程的那种“描述你想干什么,而不是描述你要怎么去实现”的编程方式感到畅快。(如果你不了解函数式编程,那在读本文之前,还请你移步去看看《函数式编程》) 好了,我们先来点感性认识,看一个Python修饰器的Hello World的代码。
Hello World
下面是代码:
文件名:hello.py
def hello(fn):
def wrapper():
print "hello, %s" % fn.__name__
fn()
print "goodby, %s" % fn.__name__
return wrapper
@hello
def foo():
print "i am foo"
foo()
</div>
当你运行代码,你会看到如下输出:
[chenaho@chenhao-air]$ python hello.py
hello, foo
i am foo
goodby, foo
</div>
你可以看到如下的东西:
1)函数foo前面有个@hello的“注解”,hello就是我们前面定义的函数hello
2)在hello函数中,其需要一个fn的参数(这就用来做回调的函数)
3)hello函数中返回了一个inner函数wrapper,这个wrapper函数回调了传进来的fn,并在回调前后加了两条语句。
Decorator 的本质
对于Python的这个@注解语法糖- Syntactic Sugar 来说,当你在用某个@decorator来修饰某个函数func时,如下所示:
@decorator
def func():
pass
</div>
其解释器会解释成下面这样的语句:
func = decorator(func)
</div>
尼玛,这不就是把一个函数当参数传到另一个函数中,然后再回调吗?是的,但是,我们需要注意,那里还有一个赋值语句,把decorator这个函数的返回值赋值回了原来的func。 根据《函数式编程》中的first class functions中的定义的,你可以把函数当成变量来使用,所以,decorator必需得返回了一个函数出来给func,这就是所谓的higher order function 高阶函数,不然,后面当func()调用的时候就会出错。 就我们上面那个hello.py里的例子来说,
@hello
def foo():
print "i am foo"
</div>
被解释成了:
foo = hello(foo)
</div>
是的,这是一条语句,而且还被执行了。你如果不信的话,你可以写这样的程序来试试看:
def fuck(fn):
print "fuck %s!" % fn.__name__[::-1].upper()
@fuck
def wfg():
pass
</div>
没了,就上面这段代码,没有调用wfg()的语句,你会发现, fuck函数被调用了,而且还很NB地输出了我们每个人的心声!
再回到我们hello.py的那个例子,我们可以看到,hello(foo)返回了wrapper()函数,所以,foo其实变成了wrapper的一个变量,而后面的foo()执行其实变成了wrapper()。
知道这点本质,当你看到有多个decorator或是带参数的decorator,你也就不会害怕了。
比如:多个decorator
@decorator_one
@decorator_two
def func():
pass
</div>
相当于:
func = decorator_one(decorator_two(func))
</div>
比如:带参数的decorator:
@decorator(arg1, arg2)
def func():
pass
</div>
相当于:
func = decorator(arg1,arg2)(func)
</div>
这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”。
带参数及多个Decrorator
我们来看一个有点意义的例子:
html.py
def makeHtmlTag(tag, *args, **kwds):
def real_decorator(fn):
css_class = " class='{0}'".format(kwds["css_class"]) \
if "css_class" in kwds else ""
def wrapped(*args, **kwds):
return "<"+tag+css_class+">" + fn(*args, **kwds) + "</"+tag+">"
return wrapped
&nbs

