装饰器

定义

  本质是函数,用来装饰其他函数(为其他函数添加附加功能)。通俗来讲:高阶函数 + 嵌套函数 = 装饰器

原则

  1.不能修改被装饰的函数的源码

  2.函数的调用方式也不能被修改

 

现存在一个函数 foo(),

import time

def foo():
    time.sleep(1)
    print('in foo')

foo()

这时,我希望添加一个新功能:记录 foo() 的运行时间。我可以新建一个嵌套函数来计算函数的运行时间

def timmer(fun):
    start = time.time()
    fun(*args, **kwargs)
    end = time.time()
    print('[DEBUG]{}`s running time:{}'.format(fun.__name__, end - start))

def foo():
    time.sleep(1)
    print('in foo')

timmer(foo)

这样确实可以实现,但是这样会修改 foo() 函数的调用,实际中这样做是不对的。

为了不修改函数的源码以及函数的调用方式,我们就会用到装饰器

 

 

最简单的装饰器

import time

def timmer(fun):
    def warpper(*args, **kwargs):
        start = time.time()
        fun(*args, **kwargs)
        end = time.time()
        print('[DEBUG]{}`s running time:{}'.format(fun.__name__, end - start))
    return warpper

@timmer    # 加上语法糖后,相当于在timmer函数中嵌套了foo函数
def foo():
    time.sleep(1)
    print('in foo')

if __name__ == '__main__':
    foo()    # 调用方式仍然是 foo()

加上装饰器@timmer后,foo() 等价于 timmer(foo)

 

带参的装饰器

def witharg(arg):    # 在最外层再套一层函数
    def decorator(fun):
        def wrapper(*args, **kwargs):
            print('outer arg: %d' % arg)
            fun()
        return wrapper
    return decorator

@witharg(10)
def foo():
    print('in foo')

if __name__ == '__main__':
    foo()

 

类装饰器

相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。此外,类装饰器还可以依靠类内部的 __call__ 方法来“装饰”函数。

class C:
    def __init__(self, fun):
        self.fun = fun

    def __call__(self):    # 修改对象状态
        self.fun()
        print('in __call__')

@C    # 类装饰器
def bar():
    print('in bar')

bar()
print(type(bar))    # <class '__main__.C'>

 

装饰器的顺序

@a
@b
@c
def F():
    print('in F')

F()    # 等价于a(b(c(F)))

 

装饰器 @wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的 docstring 、__name__。

def decorator(fun):
    def wrapper(*args, **kwargs):
        fun()
        print('in decorator')
    return wrapper

@decorator
def foo():
    print('in foo')

foo()
print(foo.__name__)    # foo被装饰器取代,__name__ 变成了 wrapper

为了避免这种情况,python中有 functools.wraps 函数

wraps本身也是个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

from functools import wraps
def decorator(fun):
    @wraps(fun)
    def wrapper(*args, **kwargs):
        fun()
        print('in decorator')
    return wrapper

@decorator
def foo():
    print('in foo')

foo()
print(foo.__name__)    # foo

 

posted @ 2020-06-01 22:09  xiaoqichaoren  阅读(109)  评论(0)    收藏  举报