装饰器

Posted xiaoqichaoren

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器相关的知识,希望对你有一定的参考价值。

定义

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

原则

  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本身也是个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

form 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

 

以上是关于装饰器的主要内容,如果未能解决你的问题,请参考以下文章

Python面向对象学习之八,装饰器

thymeleaf 片段渲染后重新加载 javascript

代码缺乏装饰?使用ts装饰器来装饰你的代码

代码缺乏装饰?使用ts装饰器来装饰你的代码

代码缺乏装饰?使用ts装饰器来装饰你的代码

代码缺乏装饰?使用ts装饰器来装饰你的代码