python装饰器---------@Decorator(一)

一、什么是装饰器?为什么要使用它?

    有时我们希望给已定存在的函数增加一些新的功能(如打印日志文件或者计时等),但是我们又不希望改动该函数的定义。这种在代码运行期间动态的增加函数功能的方式,就称为装饰器。装饰器本身代表着一种功能,用他修饰不同的函数,那么也就为这些函数增加这种功能。本质上来说,装饰器就是一个返回函数的高阶函数。

 装饰器使用@语法来修饰被装饰的函数,如下所示我们用 @dec 装饰器修饰函数 func ():

@dec
def func():
  pass

二、装饰器原理

还是用例子来解释吧。比如我们需要计算一个函数(比如下面的add)的执行时间,我们该怎么做呢?

def add(x,y):
    return x+y

ok,我们第一时间想到计算每个操作的时间,可以调用 time 模块,通过两个计时器的差就可以计算出add的执行时间,如下:

  code 1:

def add(x,y):
    return x+y


import time

start = time.time()
add(10,20)
end = time.time()

print "add(10,20) execute time is {} s" .format(end-start) 



start = time.time()
add(1,1)
end = time.time()

print "add(1,1) execute time is {} s" .format(end-start) 

但是像这样对于每个操作,我们都需要复制大段相同的代码,一点儿也不符合我们我们代码复用的风格,而且这样的写法代码可读性也不好。

思考到这儿,既然在函数执行时统计操作时间需要写很多重复的代码,那么我们直接将统计时间的代码塞进这个函数里不就好了,这样只需要在函数定义中修改一次代码,就可以在函数每次执行时输出它的执行时间(^_^是不是很有成就感),说做就做,我们来实现一下

 code 2:

def add(x,y):
    import time
    start = time.time()
    result = x + y
    end = time.time()
    print "add({},{}) execute time is {} s" .format(x,y,end-start) 
    return result



add(10,20)
add(11,2)

上面种写法在只有一个函数需要计时时已经很好用了,但是我们有很多个函数需要统计时间时,又该怎么做呢?难道要一个一个在这些函数的定义中加上类似的代码吗?这肯定不是我们的风格 。

我们来分析一下需求,多个函数需要增加相同的功能。那我们写一个计时函数,将需要计时的函数当做参数传入该函数,实现用一套代码对不同的函数进行计时的功能。

code 3:

#coding=utf-8
def add(x,y):
    result = x + y
    return x + y

def sub(x,y):
   return x-y

def multi(x,y):
    return x*y

'''计时函数'''
def timer(func,x,y):
    import time
    start = time.time()
    result = func(x,y)
    end = time.time()
    print "{}({},{}) execute time is {} s" .format(func.__name__,x,y,end-start) 
    return result


'''测试用例'''
timer(add,2,3)
timer(sub,5,3)
timer(multi,3,4)

到这里已经非常不错了。没有任何冗余代码,不过我们必须使用 timer 函数包装不同的函数,使用起来是不是总觉得有点儿麻烦。有没有更高级的写法,让我们每次调用该函数时不需要都用timer包装呢?当然有了,python高级函数的返回值可以是一个函数哦,所以我们可以用timer包装其他函数,然后返回包装后的函数。

  code 4:

#coding=utf-8
def add(x,y):
    result = x + y
    return x + y

def sub(x,y):
   return x-y

def multi(x,y):
    return x*y

'''计时函数'''
def timer(func):
    import time
    def f(x,y):
        start = time.time()
        result = func(x,y)
        end = time.time()
        print "{}({},{}) execute time is {} s" .format(func.__name__,x,y,end-start) 
        return result
    return f


'''封装'''
add = timer(add)
sub = timer(sub)
multi = timer(multi)

'''测试'''
add(1,2)
add(3,4)

sub(7,2)
multi(3,4)

 我们将code4与code3的代码仔细对比一下,看看有哪些不同。code3中每次调用一个函数都需要用timer包装一下,有点麻烦。code4中只需要在开始用timer包装一次,以后这个函数都拥有了计时功能,可以直接调用而不必每次都用timer包装了。等等code4中只需要在开始时用timer封装函数一次,以后这个函数都拥有了计时功能,这不就是开始说的装饰器的作用-----在代码运行期间动态的增加函数功能。难道code4就是传说中的装饰器?

    code4的解决方案已经非常接近装饰器的思想了,使用常见行为包装某个具体的函数,这种模式就是装饰器在做的事。但是我们忘了一点,code4中需要在开始时用timer包装一次函数,这个是不是可以简化一下呢。没错,将包装这个行为简化成@语法,就是装饰器了

code 5

#coding=utf-8

'''计时函数'''
def timer(func):
    import time
    def f(x,y):
        start = time.time()
        result = func(x,y)
        end = time.time()
        print "{}({},{}) execute time is {} s" .format(func.__name__,x,y,end-start) 
        return result
    return f


'''@name表示用name装饰器修饰下一行的函数'''

@timer
def add(x,y):
    result = x + y
    return x + y
@timer
def sub(x,y):
   return x-y
@timer
def multi(x,y):
    return x*y


'''测试'''
add(1,2)
add(3,4)

sub(7,2)
multi(3,4)

code5的写法就是一个完整的装饰器写法了,仔细对比code4和code5,是不是发现code5的写法又更简洁了一些呢。这里我们先定义了timer装饰器,以后需要给其他函数增加计时功能时,直接在该函数的定义上一行加上@timer,就直接给该函数增加了计时功能,是不是一劳永逸。

三、总结

  1、装饰器就是在代码运行期间动态的给函数增加某项功能

  2、装饰器本质上是一个返回函数的高阶函数

  3、当我们再多个函数中需要同一项功能时,可以考虑先定义一个装饰器decorate,然后在每个函数前用@decorate修饰来增加这项功能

4.请记住

@dec
def func():
  pass

等价于

dec(func)

tips:这篇博客主要讨论了装饰器的使用场合以及装饰器的设计思想。但是想完整的使用装饰器还需要考虑一些具体的问题,如:如何给参数数目不同的函数设计同一个装饰器;如何解除装饰器;如何定义带参数的装饰器等等。还有一个重要的问题,本节中定义的装饰器实际上用一个返回函数的高阶函数代替了原来的函数,这些会使得原有函数的元信息__name__,__modue__等改变,虽然大多时候这些元信息与函数的功能无关,看似无伤大雅,但是有时我们需要使用这些信息,这就造成了麻烦。

  上面所提到的这些问题,将在这篇博客Python装饰器(二)中继续探讨。

猜你喜欢

转载自blog.csdn.net/goodxin_ie/article/details/89296785
今日推荐