当前位置:   article > 正文

python装饰器(decotator)详解

decotator

###先来看看一个列子

def foo():
    print '我是lxshen'
    
foo()
输出:
我是lxshen
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这时我想在这个输出前面再执行一段程序。这时我们首先想到的是以下两种方法:

方法一:直接在函数中添加

def foo():
    print 'hello,'
    print '我是lxshen'

foo()

输出:
hello, 我是lxshen
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

方法二:我再另写一个函数,再foo函数中调用

def foo():
    fee()
    print '我是lxshen'

def fee():
    print 'hello,'
foo()

输出:
hello, 我是lxshen
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这样一看,你可能会说这两种方法好像第一种更加方便,那么我只能呵呵了,由于我这里只是举一个列子,你可以想象要添加的一段代码,代码量是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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

函数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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

foo() 可以理解为:

foo = fee(foo)
# foo先作为参数赋值给func后,foo接收指向fee返回的wrap
foo()
# 调用foo(),即等价调用wrap()
# 内部函数wrap被引用,所以外部函数的func变量(自由变量)并没有释放
# func里保存的是原foo函数对象
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

####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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在闭包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')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

上面的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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

###总结:
1.什么时候使用装饰器?

一般在开发过程中,要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,这时就要使用装饰器了。

2.使用装饰器来修饰函数

  1. 不改变原有的程序,并且可以添加新的功能
  2. 可以提高程序的可重复利用性,并增加了程序的可读性。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/213258
推荐阅读
相关标签
  

闽ICP备14008679号