python装饰器
Python 装饰器详解_python 带参数的装饰器-CSDN博客
对装饰器@wraps的解释(一看就懂)-- 并对装饰器详解 - 交流_QQ_2240410488 - 博客园 (cnblogs.com)
为什么需要装饰器呢?
Leader让小A写两个数字相加和相减的函数,小A很快就写完了:
def add(x, y):
return x + y
def sub(x, y):
return x - y
if __name__ == '__main__':
result = add(1, 2)
print(result)
result = sub(5, 4)
print(result)
# 输出:
# 3
# 1
Leader让小A添加上统计函数的运行时长的功能, 小A直接在调用函数时加上了时长的计算:
import time
def add(x, y):
return x + y
def sub(x, y):
return x - y
if __name__ == '__main__':
start = time.time()
result_1 = add(1, 2)
end = time.time()
print('result: %d' % result_1)
print('time taken %f' % (end - start))
start = time.time()
result_2 = sub(5, 4)
end = time.time()
print('result: %d' % result_2)
print('time taken %f' % (end - start))
# 输出:
# result: 3
# time taken 0.000000
# result: 1
# time taken 0.000000
Leader看了,说每次调用函数岂不是要写很多重复代码吗。小A进行了优化:
import time
def add(x, y):
start = time.time()
rv = x + y
end = time.time()
print('time taken %f' % (end - start))
return rv
def sub(x, y):
start = time.time()
rv = x - y
end = time.time()
print('time taken %f' % (end - start))
return rv
if __name__ == '__main__':
result_1 = add(1, 2)
print('result: %d' % result_1)
result_2 = sub(5, 4)
print('result: %d' % result_2)
# 输出
# time taken 0.000000
# result: 3
# time taken 0.000000
# result: 1
这种方法肯定比前一种要好。但是当我们有多个函数时,那么这似乎就不方便了。
小A又定义了一个计时的函数并包装其他函数,然后返回包装后的函数:
import time
def time_taken(func):
def inner(*args, **kwargs):
start = time.time()
rv = func(*args, **kwargs)
end = time.time()
print('time taken %f' % (end - start))
return rv
return inner
def add(x, y):
return x + y
def sub(x, y):
return x - y
if __name__ == '__main__':
add = time_taken(add) # 将函数作为参数传给另一个函数
result_1 = add(1, 2)
print('result: %d' % result_1)
sub = time_taken(sub)
result_2 = sub(5, 4)
print('result: %d' % result_2)
# 输出:
# time taken 0.000000
# result: 3
# time taken 0.000000
# result: 1
Leader说上面的解决方案以及非常接近装饰器的思想了,小A查了一下装饰器的用法,加入装饰器后代码果然变得很优雅。
import time
def time_taken(func):
def inner(*args, **kwargs):
start = time.time()
rv = func(*args, **kwargs)
end = time.time()
print('time taken %f' % (end - start))
return rv
return inner
@time_taken
# @time_taken等价于add = time_taken(add)
def add(x, y):
return x + y
@time_taken
def sub(x, y):
return x - y
if __name__ == '__main__':
result_1 = add(1, 2)
print('result: %d' % result_1)
result_2 = sub(5, 4)
print('result: %d' % result_2)
# 输出:
# time taken 0.000000
# result: 3
# time taken 0.000000
# result: 1
类装饰器
装饰器本质上是一个 Python 函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
不带参数的类装饰器
基于类装饰器的实现,必须实现__call__
和__init__
两个内置函数。
__init__
:接收被装饰函数
__call__
:实现装饰逻辑
class logger(object):
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print 'the function {}() is running...'\
.format(self.func.__name__)
return self.func(*args,**kwargs)
@logger
def say(something):
print 'say {}!'.format(something)
say('hello')
运行结果如下:
Copythe function say() is running...
say hello!
带参数的类装饰器
带参数和不带参数的类装饰器有很大的不同。
__init__
:不再接收被装饰函数,而是接收传入参数
__call__
:接收被装饰函数,实现装饰逻辑
class logger(object):
def __init__(self,level='INFO'):
self.level = level
def __call__(self,func):
def wrapper(*args,**kwargs):
print '{level}: the function {func} () is running...'\
.format(level=self.level, func=func.__name__)
func(*args,**kwargs)
return wrapper
@logger(level='WARNING')
def say(something):
print 'say {}!'.format(something)
say('hello')
运行结果如下:
CopyWARNING: the function say () is running...
say hello!
wraps语法糖
使用这个语法糖保留函数原来的属性,name,__doc__等属性
from functools import wraps
def logging(func):
@wraps(func)
def inner(*args, **kwargs):
"""logging.inner"""
if type == 'debug':
print('[DEBUG] logging')
else:
print('[INFO] logging')
rv = func(*args, **kwargs)
return rv
return inner
@logging
def add(x, y):
"""Add x and y"""
return x + y
if __name__ == '__main__':
print(add.__name__)
print(add.__doc__)
# 输出:
# add
# Add x and y
打印日志
# 在增加一层函数
from functools import wraps
import time
from random import randint
def record(output):
def use_time(func):
@wraps(func)
def wrapper(*args,**kwargs):
st_time = time.time()
result = func(*args,**kwargs)
end_time = time.time()
# print(f'{func.__name__}函数use_time:{end_time-st_time}s')
output(func.__name__, end_time-st_time)
return wrapper
return use_time
# 改装饰器的结果就可以自定义了,下面以print函数为例
@record(print)
def foo():
time.sleep(randint(2,5))
for _ in range(3):
foo()
>>>>
foo 3.000645875930786
foo 4.003818988800049
foo 2.0020666122436523
# 自己定义函数,输入日志文件
def write_log(name,content):
with open('./time.log','a')as f:
f.write(f'{name}耗时:{content}\r\n') # \r\n 换行
# 只需要将装饰器改为@record(write_log)
@record(write_log)
def foo():
time.sleep(randint(2,5))
for _ in range(3):
foo()