https://segmentfault.com/a/1190000038959829 https://www.cnblogs.com/wongbingming/p/10934356.html


  • @ 符号就是装饰器的语法糖
  • 它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为装饰函数 或 装饰器

装饰器的使用方法很固定:

  • 先定义一个装饰函数(帽子)(也可以用类、偏函数实现)
  • 再定义你的业务函数、或者类(人)
  • 最后把这顶帽子带在这个人头上

装饰器的简单的用法有很多,这里举两个常见的:

  • 日志打印器

不带参数的装饰器

  1. def logger(func):
  2. def wrapper():
  3. print('函数{}准备运行...'.format(func.__name__))
  4. # 执行add()
  5. func()
  6. print('函数{}运行结束...'.format(func.__name__))
  7. return wrapper
  8. @logger
  9. def add():
  10. print('计算函数')
  11. if __name__ == '__main__':
  12. add()
  • @logger是将add( )函数作为参数传入函数logger中,返回一个wrapper函数
  • wrapper函数先执行print('函数{}准备运行...'.format(func.__name__));然后执行fun(),即调用传入的add();最后wrapper函数执行print('函数{}运行结束...'.format(func.__name__))
  • return wrappe是将函数作为返回值返回
  • add()调用函数 ```python

    结果

函数add准备运行… 计算函数 函数add运行结束…

  1. <a name="DvYOd"></a>
  2. # 带参数的装饰器
  3. ```python
  4. def logger(func):
  5. def wrapper(*args,**kwargs):
  6. print('函数{}准备运行...'.format(func.__name__))
  7. # 执行add()
  8. func(*args,**kwargs)
  9. print('函数{}运行结束...'.format(func.__name__))
  10. return wrapper
  11. @logger
  12. def add(x,y):
  13. sum = x + y
  14. print('计算结果是{}'.format(sum))
  15. if __name__ == '__main__':
  16. add(1,2)
  • 通常情况下,会把args和*kwargs,作为装饰器内部函数wrapper()的参数。 表示接受任意数量和类型的参数
  • 注意 wrapper 的 args 与 **kwargs 参数,这是必须的, args 表示所有的位置参数,**kwargs 表示所有的关键字参数。之后再将其传到 func函数中, 这样保证了能完全传递所有参数。 ```python

    结果

函数add准备运行… 计算结果是3 函数add运行结束…

<a name="zxyHz"></a>
# 不带参数的类装饰器
基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。<br />__init__ :接收被装饰函数<br />__call__ :实现装饰逻辑。

```python
class Logger(object):
    def __init__(self, func):
      self.func = func

    def __call__(self):
        print('函数{func}准备运行...'.format(func = self.func.__name__))
        # 执行add()
        return self.func()



@Logger
def add():
    print('计算函数')


if __name__ == '__main__':
    add()

带参数的类装饰器

class Logger(object):
    def __init__(self, func):
      self.func = func

    def __call__(self,*args,**kwargs):
        print('函数{func}准备运行...'.format(func = self.func.__name__))
        # 执行add()
        return self.func(*args,**kwargs)



@Logger
def add(x,y):
    sum = x + y
    print('计算结果{}'.format(sum))


if __name__ == '__main__':
    add(1,2)

wraps 装饰器

一个函数不止有他的执行语句,还有着 name(函数名),doc (说明文档)等属性,我们之前的例子会导致这些属性改变

def logger(func):
    def wrapper():
        print('函数{}准备运行...'.format(func.__name__))
        # 执行add()
        func()

        print('函数{}运行结束...'.format(func.__name__))

    return wrapper

@logger
def add():
    print('计算函数')

if __name__ == '__main__':
    print(add.__name__)


# 结果
wrapper

由于装饰器返回了 wrapper 函数替换掉了之前的add 函数,导致函数名变成了 wrapper 函数的了。
解决这一问题的办法是通过 functools 模块下的 wraps 装饰器

from functools import wraps

def logger(func):
    @wraps(func)
    def wrapper():
        print('函数{}准备运行...'.format(func.__name__))
        # 执行add()
        func()

        print('函数{}运行结束...'.format(func.__name__))

    return wrapper

@logger
def add():
    print('计算函数')


if __name__ == '__main__':
    print(add.__name__)


# 结果
add