目录
前言
python装饰器是一种特殊的函数,其返回值是一个函数,在不更改原函数代码前提下给原函数增加新的功能。
传统做法
定义以下原函数,以增加该函数的计时功能为例。
import time
def func():
print("hello",end=" ")
time.sleep(1)
print("world!")
如果想记录下这个函数执行的总时间,那最简单直接的做法就是修改原函数。
import time
def func():
startTime = time.time()
print("hello",end=" ")
time.sleep(1)
print("world!")
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
func()
hello world time is 1001 ms
函数作为参数传递
如果要记录多个不同函数的总的执行时间就需要一个个修改,测试结束可能又不需要时间记录功能,又要改回。这样不仅增加了工作量,也增加的出错的可能性。
把要计时的函数(func)作为参数传递给计时函数(deco),也可以实现不修改原函数,增加计时功能。
#避免直接侵入原函数修改,但是生效需要再次执行函数
import time
def deco(func):
startTime = time.time()
func()
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
def func():
print("hello")
time.sleep(1)
print("world")
deco(func)#只有把func()作为参数,新加入功能才会生效
hello world time is 1004 ms
原函数功能未变
func()
hello world!
装饰器
以上方法需要在调用函数的位置修改函数调用为计时函数调,通过计时函数间接调用原函数,同样不需要计时功能时又要改回。
如何做到既不需要修改原函数,也不需要修改函数的调用方法,很简单的增加函数的功能--装饰器。
import time
def deco(func0):
def wrapper(): #函数中定义函数
startTime = time.time()
func0()
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper #返回函数
@deco
def func():
print("hello",end=" ")
time.sleep(1)
print("world!")
func()
hello world! time is 1004 ms
带参装饰器
需要时在函数定义前加上装饰器,不需要时去掉装饰器,而且根据需要还可以加多个装饰器,装饰器可以是带参(可以是定参、不定参数)也可以是不带参。
带定参数的装饰器
只能作为参数数量与装饰器数量一致的函数的装饰器。
import time
def deco(func):
def wrapper(a,b): #装饰器两个参数
startTime = time.time()
func(a,b)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper
@deco
def func1(a,b): #原函数1两个参数
print("a+b=")
time.sleep(1)
print((a+b))
@deco
def func2(a,b): #原函数2两个参数
print("a-b=")
time.sleep(2)
print((a-b))
func1(3,5)
a+b=8 time is 1007 ms
func2(8,5)
a-b=3 time is 2016 ms
带有不定参数的装饰器
可以作为不同数量参数函数的装饰器。
import time
def deco(func):
def wrapper(*args, **kwargs):
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapper
@deco
def func3(a,b):
print("a+b=",end="")
time.sleep(1)
print((a+b))
@deco
def func4(a,b,c):
print("a+b+c=",end="")
time.sleep(1)
print((a+b+c))
func4(3,4,5)
func3(3,4)
a+b+c=12 time is 1004 ms a+b=7 time is 1012 ms
多个装饰器
前面的装饰器在外层,后面的在内层。
import time
def deco01(func):
def wrapper(*args, **kwargs):
print("this is deco01")
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
print("deco01 end here")
return wrapper
def deco02(func):
def wrapper(*args, **kwargs):
print("this is deco02")
func(*args, **kwargs)
print("deco02 end here")
return wrapper
@deco01
@deco02
def func5(a,b):
print("a*b=",end="")
time.sleep(1)
print(a*b)
func5(3,4)
print(func5.__name_)
this is deco01 this is deco02 a*b=12 deco02 end here time is 1004 ms deco01 end here wrapper
functools.wraps
以上做法原函数被warpper替代了,它重写了函数的名字和注释文档(docstring)。Python提供一个简单的函数来解决这个问题,那就是functools.wraps。
使用functools.wraps
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction(*args, **kwargs):
startTime = time.time()
func(*args, **kwargs)
endTime = time.time()
msecs = (endTime - startTime)*1000
print("time is %d ms" %msecs)
return wrapTheFunction
@a_new_decorator
def func8(a,b):
print("a*b=",end="")
time.sleep(1)
print(a*b)
func8(2,5)
print(func8.__name__)
a*b=10 time is 1002 ms func8
装饰器类
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile文件,写入
with open(self.logfile, 'a') as opened_file:
# 现在将日志输出到指定的文件
opened_file.write(log_string + '\n')
# 发送一个通知
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
# logit只输出日志
pass
@logit()
def myfunc1():
pass
装饰器使用
授权(Authorization)
装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
日志(Logging)
日志是装饰器运用的另一个亮点。这是个例子:
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
# Output: addition_func was called