赞
踩
###先来看看一个列子
def foo():
print '我是lxshen'
foo()
输出:
我是lxshen
这时我想在这个输出前面再执行一段程序。这时我们首先想到的是以下两种方法:
方法一:直接在函数中添加
def foo():
print 'hello,'
print '我是lxshen'
foo()
输出:
hello, 我是lxshen
方法二:我再另写一个函数,再foo函数中调用
def foo():
fee()
print '我是lxshen'
def fee():
print 'hello,'
foo()
输出:
hello, 我是lxshen
这样一看,你可能会说这两种方法好像第一种更加方便,那么我只能呵呵了,由于我这里只是举一个列子,你可以想象要添加的一段代码,代码量是100行甚至更多,并且要在几十个函数中添加。那么这时候你会选择哪个方法?反正我是选择第二种,不然会死人的。
###装饰器
这样你以为就完了吗,一般在开发过程中,要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
def fee(func): # 需要传递一个函数
def wrap(): # 如果这个函数需要传递参数,那么所需要参数就写在这里
print 'hello,'
return func()
return wrap
def foo():
print '我是lxshen'
f = fee(foo) # f 等价于 wrap
f() # wrap()
输出:
hello, 我是lxshen
函数fee就是装饰器,它把执行真正业务逻辑代码的foo包裹在函数里面,看起来像bar被use_logging装饰了。在这个例子中,函数进入和对出是,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)
使用’语法糖’@
@符号是装饰器的’语法糖’,在定义函数的时候使用,避免再一次赋值操作
def fee(func):
def wrap():
print 'hello,'
func()
return wrap
@fee
def foo():
print '我是lxshen'
foo()
输出:
hello, 我是lxshen
foo() 可以理解为:
foo = fee(foo)
# foo先作为参数赋值给func后,foo接收指向fee返回的wrap
foo()
# 调用foo(),即等价调用wrap()
# 内部函数wrap被引用,所以外部函数的func变量(自由变量)并没有释放
# func里保存的是原foo函数对象
如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。
####2.被装饰的函数有参数
def fee(func): def wrap(gold): print 'hello,' func(gold) return wrap @fee def foo(gold): print gold print '我是lxshen' @fee def faa(gold): print gold print '我是xiaoming' foo('大家好') faa('lxshen') 输出: hello, 大家好 我是lxshen hello, lxshen 我是xiaoming
在闭包wrap中执行func函数(即执行foo函数),既然func需要一个参数,那么闭包warp就需要一个添加参数。如果还是不理解,可以把 这个例子拆开成‘一个简单的装饰器’的样子来看。
####3.修饰器带参数
def user(name): def fee(func): def wrap(gold): if name == 'lxshen': print 'hello,' elif name == 'xiaoming': print '你好,' func(gold) return wrap return fee @user(name='lxshen') def foo(gold): print gold print '我是lxshen' @user(name='xiaoming') def faa(gold): print gold print '我是xiaoming' foo('大家好') faa('lxshen')
上面的user是允许带参数的装饰器。他实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解我一个含有参数的闭包。
####4.类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的。
class Fee(object): def __init__(self,func): self._func = func def __call__(self, name): print 'hello' self._func(name) @Fee def foo(gold): print gold print 'lxshen' foo('大家好') 输出: hello 大家好 lxshen 分析: #1.当用Fee来装作装饰器对foo函数进行装饰的时候,首先会创建Fee的实例对象 # 并且会把foo这个函数名当做参数传递到__init__方法中 # 即在__init__方法中的func变量指向了foo函数体 #2. foo函数相当于指向了用Fee创建出来的实例对象 # #3. 当在使用foo()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法 # #4. 为了能够在__call__方法中调用原来foo指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用 # 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到foo之前的函数体
###总结:
1.什么时候使用装饰器?
一般在开发过程中,要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,这时就要使用装饰器了。
2.使用装饰器来修饰函数
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。