python装饰器

基于开放封闭的原则,对现有的函数想要扩展新功能,则不推荐对源码进行修改,所以装饰器就是为了扩展函数功能所用。

#原函数

def foo():
    print("foo...")
    time.sleep(2)

#现在需要添加的功能是:为函数计时

#如果在原函数进行修改,则为

import time
def foo():
    start = time.time()
    print("foo...")
    time.sleep(2)
    end = time.time()
    print("spend %s" %(end - start))
foo()

########
foo...
spend 2.0001144409179688

#但是如果有成千上万的函数,逐个修改的工程量极大,所以可以修改为

import time
def foo():
    print("foo...")
    time.sleep(2)

def show_time(f):
    start = time.time()
    f()
    end = time.time()
    print("spend %s" %(end - start))

show_time(foo)

#通过另外定义一个函数,将原函数传入,这样对于每个函数则不用重新定义,只需要再传入即可

#但是,现在使用原函数时,不再是使用原函数名来调用,这样在生产环境中会有较大影响

#所以需要使函数调用仍为之前的,但功能是扩展的

def show_time(f):
    def inner():
        start=time.time()
        f()
        end=time.time()
        print("spend %s" %(end-start))
    return inner
foo=show_time(foo)
foo()

########
foo...
spend 2.0001142024993896

#通过返回一个扩展后的函数给源函数接收,这样就可调用原有的函数名,还扩展了功能

 

#在python中,还对装饰器进行了一个优雅的设置

@show_time

#这个为python的一个语法糖,起作用等同于foo=show_time(foo),在原函数前添加此句即实现了功能扩展

#小结:

1.为了实现扩展功能,需要重新定义一个装饰器函数,在装饰器中有一个内部函数inner主要用于功能扩展,且在最后也需要返回inner函数供原函数接收

2.在装饰器中,使用了高阶函数和闭包的概念。其中传入原函数和返回inner函数则是高阶函数的定义,而inner函数块和inner函数中调用传入的原函数方法则是闭包的概念

3.调用装饰器函数,只需要在原函数前加 @装饰器函数名  即可

4.装饰器的实际原理,@语法糖的作用,同下图所示

 

 

#以上仅是装饰器的基础使用

#如果原函数需要传入参数,则需要在装饰器的内部函数inner时定义

import time
def show_time(f):
    def inner(*a,**b):
        start = time.time()
        f(*a,**b)
        end = time.time()
        print("spend %s" %(end - start))
    return inner

@show_time
def add(*x,**y):
    sums = 0
    for i in x:
        sums += i
    print(sums)
    time.sleep(1)

add(1,5,6,8)

########
20
spend 1.0000574588775635

#如果是装饰器需要传入参数,则需要再原装饰器外部再定义一层函数,以便传入参数

import time
def logger(flag=''):
    def show_time(f):
        def inner():
            start = time.time()
            f()
            end = time.time()
            print("spend %s" %(end - start))
            if flag=='true':
                print("logs...")
        return inner
    return show_time

@logger('true')
def foo():
    print("foo...")
    time.sleep(2)
foo()

########
foo...
spend 2.0001144409179688
logs...

 

#总结:

1.如果是原函数有参数传入,则在装饰器中的内部函数inner也需要有相同的形参定义

2.如果是装饰器函数需要参数传入,则需要在装饰器函数外部再添加一层函数,以传入参数给装饰器函数使用,且调用时使用的是外层函数而不再是原装饰器函数

posted @ 2018-03-26 11:58  jianbonet  阅读(144)  评论(0编辑  收藏  举报