- - 看了两三篇博客。
其中有一篇虽然
* 错字连篇
* 代码错误
* 逻辑混乱
但是将decorator 循序渐进实现的思路值得记录一下。 故在此回顾一下什么是decorator。
为什么要使用装饰器
- 并不希望修改原有函数的代码
- 并不希望修改调用原有函数的方式
- 希望在满足1,2的前提下给原函数增加新的功能
有点像java的aop的感觉。
我们逐步看一下如何实现…
假设我们有个函数
process(*args)
我们希望在调用的时候对他进行计时,打印出来这个函数的执行时间。
以下写法全部都不会改写process(*args)
本身
solution1
import time
def process(): # 实际的处理函数,具体做什么我们不关心
time.sleep(2)
def wrapper():
print('开始执行... ')
start = time.time()
process()
end = time.time()
print('耗时 %s s' %(end-start))
wrapper()
执行结果
开始执行...
耗时 2.0006916522979736 s
这种看似没什么问题,却违背了我们 2. 并不希望修改调用原有函数的方式 这一原则。 原本我们调用process()
只需要 process()
就好了,现在变成了wrapper()
或者说可以包装进去成 wrapper(process)
。
solution2
# 略去process() 的定义 以及import部分
def decorator(func):
def wrapper(*args, **kwargs): # 表示process函数中可以任意入参
print('开始执行... ')
start = time.time()
func(*args, **kwargs)
end = time.time()
print('耗时 %s s' %(end-start))
return wrapper
process = decorator(process)
process()
执行结果
开始执行...
耗时 2.0006916522979736 s
这种调用方式就足够完美了,通过这种方式我们初步实现了装饰器。
process = decorator(process)
这个地方将process作为入参传入装饰器并封存等待调用。
更高级的写法是
def decorator(func):
def wrapper(*args, **kwargs):
print('开始执行... ')
start = time.time()
func(*args, **kwargs)
end = time.time()
print('耗时 %s s' %(end-start))
return wrapper
@decorator
def process():
time.sleep(2)
process()
注解的作用相当于 process = decorator(process)
solution3
如果process有返回值怎么办?
目前的写法只能返回wrapper,然后让wrapper()
去调用到内部的process()
,返回值也会消耗在wrapper()
的内部。
def decorator(func):
def wrapper(*args, **kwargs):
print('开始执行... ')
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print('耗时 %s s' % (end - start))
return res
return wrapper
@decorator
def process():
time.sleep(2)
return 'OMG'
print(process())
结果是:
开始执行...
耗时 2.0006561279296875 s
OMG
还不够完美,@注解的方式还可以注入参数
final solution
def timer(text):
def decorator(func):
def wrapper(*args, **kwargs):
print('%s 开始执行... ' % text)
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print('%s 耗时 %s s' % (text,(end - start)))
return res
return wrapper
return decorator
@timer('无敌YF')
def process():
time.sleep(2)
return 'OMG'
print(process())
如果还需要在某个新的层级上添加新的入参,还需要再嵌套一层。
如上的写法相当于
timer = timer('无敌YF')
process = decorator(process) # 需要好好对照代码去理解。。
思考题:
在final solution的代码中
print(process.__name__)
结果会是什么呢??
Answer is
wrapper
这是不对的,我们的process的属性经过包装后发生了变化,返回的是wrapper的__name__
, 尽管这是很小的一点,但也是不正确的是错误的。我们需要修正这一点。
Real Final Solution
import time
import functools
def timer(text):
def decorator(func):
@functools.wraps(func) # 将func的相应属性赋值给wrapper
def wrapper(*args, **kwargs):
print('%s 开始执行... ' % text)
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print('%s 耗时 %s s' % (text,(end - start)))
return res
return wrapper
return decorator
@timer('无敌YF')
def process():
time.sleep(2)
return 'OMG'
print(process())
print(process.__name__) # 此时就会如实的打印出process
就此,Decorator的介绍就算是正式结束了。