@会网络的老鼠

涂飞平的博客空间

Python的Decorator [原]

4 年前 0

Python语言中的Decorator,也就是常说的装饰器,个人认为是Python语言中的亮点之一,在语言层面直接支持装饰器的我所知道的也就是Python了,虽然Javascript也是支持的,但需要我们自己将函数赋值给同名的变量以达到相同的效果,而Python虽然在原理上是一致的,但其在语言层面直接支持(提供了@运算符来标识),更直观,更简洁,还有,更炫~~

对于Decorator的作用,可以用一句话概括:函数的替换,就是将一个新函数替换原来的函数,由于Python和Javascript中的函数都是对象,所以可以赋值给指定的变量,这个变量名 就是我们所说的函数名。这有什么用处呢?

可以修改原有函数的bug,在作为框架或者产品代码的库中,也许已经大量调用了你写的某个函数,但该函数有问题,这个时候,如果能重新修改该函数,且不影响原有代码就最好了,Decorator就可以解决该问题;

可以扩展原有函数的功能,如果你想给你已经写好的函数添加一些新的功能(比如调用日志) 你可以使用新的函数替换原有函数(当然,在新的函数中还是要调用原有的函数),然后将该新函数替换原有函数就可以解决这个问题,Decorator也是完美的解决方案。

在Javascript中,我们需要扩展一个名为test的函数,比如

var test = function() {
console.log("hello world");
}
给它加上日志功能,代码如下
decorator = function(fn) {
return function(){
console.log("before test");
fn();
console.log("after test");
}
}
然后,替换的时候,只要完成一次调用即可:
test = decorator(test);
其中的fn就是传入的函数对象,在返回的新的函数对象中,fn会保存在作用域链中(闭包原理,同样适用于python)

而调用的时候,完全可以不用去理会其中函数的细节是否发生变化

//...其他代码 
test();
//...其他代码
在Python中,也可以举出一模一样的例子来:
def test():
print "hello world"
也给他加上日志功能,代码如下:
def decorator(fn):
def newTest():
print "before"
fn()
print "after"
return newTest
类似的替换方式:
 test = decorator(test)
然后,也是同样的调用方式:
#...其他代码 
test()
#...其他代码
在Python中,有个更优雅的语法来完成替换过程的调用,比如上面的代码:
test = decorator(test)
可以修改一下,在test函数定义的地方,直接加上@decorator就OK了:
@decorator 
def test():
知道原理之后,就可以理解Python库中的各种装饰器调用,概括起来还是:用新函数替换旧函数,并在新函数中调用旧函数

下面简单分析几种Python中的装饰器使用方式:

1、简单的装饰器,没有参数,只是做简单的工作,如上面的例子所示;

2、带有参数的装饰器,带有参数,根据参数来修改工作函数的行为,比如Bottle中使用Route装饰器来分发访问请求(以下代码摘自http://sunnytu.sinaapp.com的,该应用采用Bottle)

@app.route('/html/:fn') 
def sundy_static(fn):
return static_file(fn, root="static")
过多的分析,可能会把人搞晕,我还是直接写出route的示意代码吧:
def route(url_pattern):
def newroute(fn):
def newproc():
#process with url_pattern, get params
params = xxx
return fn(params)
return newproc
return newroute
而@route('/html/:fn')所做的工作如下:
sundy_static = route('/html/:fn')(sundy_static)
Bottle会将原始的sundy_static加入路由列表,以便在处理请求的时候找到所需的处理例程

3、对象方法的装饰器,其实与一般方法类似,只是要注意特殊参数self,比如:

class test(): 
def sayHello(self):
print "Hello world"

def decorator(fn):
def newHello(self):
print "before"
fn(self)
return newHello

如何使用呢? 直接在类定义中,方法前面加入该装饰器即可:
class test(): 
@decorator
def sayHello(self):
Python有一些比较常用的Decorator,比如staticmethod, classmethod等 关于闭包,你可以参考这个链接
Decorator应用模式,现在有个更学术化的名字,AOP(面向切/方面编程),Javscript和Python是原生支持AOP的,Java/C#通过工具可以达到相同的效果。

编写评论