python装饰器(decotator)详解

###先来看看一个列子

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.使用装饰器来修饰函数

  1. 不改变原有的程序,并且可以添加新的功能
  2. 可以提高程序的可重复利用性,并增加了程序的可读性。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值