https://segmentfault.com/a/1190000038959829 https://www.cnblogs.com/wongbingming/p/10934356.html
- @ 符号就是装饰器的语法糖
- 它放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上。和这个函数绑定在一起。在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶上这顶帽子,这顶帽子我们称之为装饰函数 或 装饰器
装饰器的使用方法很固定:
- 先定义一个装饰函数(帽子)(也可以用类、偏函数实现)
- 再定义你的业务函数、或者类(人)
- 最后把这顶帽子带在这个人头上
装饰器的简单的用法有很多,这里举两个常见的:
- 日志打印器
不带参数的装饰器
def logger(func):
def wrapper():
print('函数{}准备运行...'.format(func.__name__))
# 执行add()
func()
print('函数{}运行结束...'.format(func.__name__))
return wrapper
@logger
def add():
print('计算函数')
if __name__ == '__main__':
add()
@logger
是将add( )函数作为参数传入函数logger中,返回一个wrapper函数- wrapper函数先执行
print('函数{}准备运行...'.format(func.__name__))
;然后执行fun()
,即调用传入的add()
;最后wrapper函数执行print('函数{}运行结束...'.format(func.__name__))
return wrappe
是将函数作为返回值返回add()
调用函数 ```python结果
函数add准备运行… 计算函数 函数add运行结束…
<a name="DvYOd"></a>
# 带参数的装饰器
```python
def logger(func):
def wrapper(*args,**kwargs):
print('函数{}准备运行...'.format(func.__name__))
# 执行add()
func(*args,**kwargs)
print('函数{}运行结束...'.format(func.__name__))
return wrapper
@logger
def add(x,y):
sum = x + y
print('计算结果是{}'.format(sum))
if __name__ == '__main__':
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