1、概览
装饰器可以帮助我们为已经存在的对象添加额外的功能
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事物处理、缓存、权限校验等场景。
1.1、为now函数 加一行日志
# 定义now函数
def now():
print('2018-5-8')
# 编辑decorator
def log(func): # 接受函数名
def wrapper(*args,**kw): # 接收传入函数的参数
print('the function name is %s():' % func.__name__) # 打印函数的__name__属性,注意__name__ 是两条_(下划线)
return func(*args,**kw) # 返回这个函数
return wrapper
# 将decorator 加到 now函数
@log
def now():
print('2018-5-8')
把@log放到now()函数的定义处,相当于执行了 now=log(now)。过程为【wapper=log(now),weapper返回now()】
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
1.2、三层嵌套的decorator
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数。比如,要自定义log的文本
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
# 添加到now() 函数
@log('execute')
def now():
print('2015-3-25')
# 效果
>>> now()
execute now():
2015-3-25
三层嵌套的执行效果:now = log('execute')(now)
1.3、原始函数的属性复制
从上面的例子可以看出,now()函数 最终是由wrapper 函数返回的。 此时now() 函数的__name__属性已经变为了 'wrapper'
>>> now.__name__
'wrapper'
但我们的本意只是,给now() 函数加额外功能,并不想改变它本身的属性,此时,就需要把now() 函数的属性复制给 wrapper() 函数
不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的
functools.wraps:wraps 本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中。这使得装饰器函数也有和原函数一样的元信息
# 顺便复制属性的decorator
import functools
def log(func):
@functools.wraps(func) #这个装饰器始终在 wrapper 函数上面
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
2、例子
1、请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
# -*- coding: utf-8 -*-
import time, functools
def metric(fn):
@functools.wraps(fn) # 复制原函数信息 到 wrapper
def wrapper(*args, **kw):
begin=time.time()
res=fn(*args, **kw) # 将fn 函数的返回值 赋给 res
end=time.time()
print("%s executed in %s ms" % (fn.__name__,(end-start)*1000)) # 打印执行时间 ms
return res
return wrapper
2、在函数的执行前后打印标记
def log(func):
def call(*args,**kw):
print('begin call')
out=func(*args,**kw) # 要点在于 将函数的返回值 赋值给一个变量,最后return 变量
print('end call')
return out
return call
原文地址:http://blog.51cto.com/12758568/2116747