###先来看看一个列子
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行甚至更多,并且要在几十个函数中添加。那么这时候你会选择哪个方法?反正我是选择第二种,不然会死人的。
###装饰器
这样你以为就完了吗,一般在开发过程中,要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块
- 开放:对扩展开发
这时就不得不说下装饰器了。
如果你不懂闭包,我建议你先看看闭包的用法
http://blog.csdn.net/lowerxiaoshen/article/details/78568656
一个简单的装饰器:
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.使用装饰器来修饰函数
- 不改变原有的程序,并且可以添加新的功能
- 可以提高程序的可重复利用性,并增加了程序的可读性。