一、装饰器的概念
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
装饰器的作用是在原有的函数基础上外包一个函数,增加新的功能。
本质上就是一个返回函数的高阶函数。
二、装饰器的原理
装饰器是在原来函数基础上外包一个函数,增加新功能。
如果:
原函数A
则:
装饰器D可为:
def D(A):
def W(*args,**kw):
#新功能
return A(*args,**kw)
return W
三、一个log函数的例子
下面我们来看一个log函数的经典装饰器例子。
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
上述函数的设计逻辑是:
step1,将func函数作为参数传入log,
step2,在log函数中内置一个函数wrapper作为增加功能的函数,并计算(当然,本例中只是函数名打印出来)
step3,以func作为返回值,完成wrapper任务。
step4,最后,返回wrapper函数。
wrapper函数携带的是新功能(那句打印print函数名)+原本的func函数执行结果。
四、装饰器调用
接下来,我们看看装饰器log如何使用。
利用python的@语法将log置于函数定义前
@log
def now():
print(‘2019-11-27’)
其中,
@log相当于
now = log(now)
运行now()则打印如下结果
call now():
2019-11-27
五、传入参数的装饰器
decorator本身也可以传入参数
需要外层再进行包装
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(‘2019-11-27’)
执行now()
结果为:
execute now():
2019-11-27
上面三层嵌套的原理如下:
@log(‘execute’)
def now():……
相当于:
def now():……
now = log('execute')(now)
我们剖析上面的语句
首先执行log('execute'),返回的是decorator函数。
再调用返回的函数,参数是now函数,返回值最终是wrapper函数。
六、decorator的隐含问题
经过修饰的函数__name__发生了改变。
>>>now.__name__
‘wrapper’
有些依赖函数签名的代码执行就会出错。
解决方案
1.wrapper.__name__ = func.__name__ (不推荐)
2.Python内置的functools.wraps
代码如下:
不带参数的装饰器:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
带参数的装饰器
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
七、实例:请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间
# -*- coding: utf-8 -*-
import time, functools
#请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
def metric(fn):
@functools.wraps(fn)
def wrapper(*args, **kw):
sta_time = time.time()
func = fn(*args, **kw)
end_time = time.time()
print('%s executed in %s ms' % (fn.__name__, end_time - sta_time))
return func
return wrapper
# 测试
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
学会了就点个赞吧。