什么是装饰器
在函数调用前后自动打印日志,但又不希望修改函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| def log(func): def wrapper(*args,**kw): print('call %s():' % func.__name__) return func(*args,**kw) return wrapper @log def now(): print('2015-01-01') now() ```
输出结果
```python
call now(): 2015-01-01 ``` 把@log放到now()函数的定义处,相当于执行了语句: ```python
now = log(now) ``` 如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
```python
def log(text): def decorator(func): def wrapper(*args,**kw): print('%s %s():' % (text,func.__name__)) return func(*args,**kw) return wrapper return decorator @log('execute') def now(): print('2015-01-01') now() ```
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
```python >>> now = log('execute')(now) ``` 首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。
以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper': ```python print(now.__name__)
|
输出
因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的name等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.name = func.name这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper @log def now(): print('2015-01-01') now() ```
或者针对带参数的decorator:
```python import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text,func.__name__)) return func(*args, **kw) return wrapper return decorator @log('tt') def now(): print('2015-01-01') now()
|
输出
喜欢新事物,关注云计算行业,对新的技术有追求,喜欢写作和coding