装饰器:
-
把一个函数当作参数,返回一个替代版的函数
-
本质上就是一个返回函数的函数
-
在不改变原函数的基础上,给函数增加功能
import time
def decorator(func): ##wrapper里不写东西不能接收参数
def wrapper():
print(time.time()) ##打印系统运行的时间
func() ##调用传进来的函数,也就是f1()——>打印:This is a function
return wrapper ##如果不返回,则不会执行这个wrapper函数
@decorator
def f1():
print('This is a function')
f1()
结果:
1547992084.6096034
This is a function
##wrapper里想要接收参数
import time
def decorator(func):
def wrapper(*args,**kwargs): ##表示接收任意多个参数,也可以接收字典
print(time.time())
func(*args,**kwargs) ##此处接收的参数与wrapper一致
return wrapper
@decorator
def f3(func_name1,func_name2,**kwargs):
print('This is function ' + func_name1)
print('This is function ' + func_name2)
print(kwargs)
f3('test1','test2',a=1,b=2,c='westos')
结果:
1547992404.5991712
This is function test1
This is function test2
{'a': 1, 'b': 2, 'c': 'westos'}
装饰器实现一个函数计时器
- 比较用for循环拼接,和系统内置拼接函数.join的速度
import time
import random
import string
import functools
li = [random.choice(string.ascii_letters) for i in range(10)]
print(li)
def timeit(fun):
@functools.wraps(fun) ##为了保留被修饰的函数名和帮助信息文档
def wrapper(*args,**kwargs):
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('运行时间为:%.6f' %(end_time - start_time))
return res
return wrapper
@timeit
def con_add():
"""这是for循环"""
s = ''
for i in li:
s += (i + ',')
print(s)
@timeit
def join_add():
"""这是join方法"""
print(','.join(li))
con_add()
join_add()
结果:
['O', 'e', 'P', 'g', 'D', 'S', 'L', 'z', 'D', 'g']
O,e,P,g,D,S,L,z,D,g,
运行时间为:0.000014
O,e,P,g,D,S,L,z,D,g
运行时间为:0.000007
装饰器问题1:被修饰的函数有返回值怎么办
- 错误做法:
def timeit(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
fun(*args,**kwargs)
end_time = time.time()
print('系统的运行时间为:%.6f' %(end_time - start_time))
return wrapper
@timeit
def fun3(x):
return x ** 2 ##被装饰的函数有返回值,则需要在装饰器中将函数的返回值传给一个参数,并返回这个参数,否则返回值为None
a = fun3(3)
print(a)
结果:
系统的运行时间为:0.000003
None
- 正确做法:
def timeit(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('系统的运行时间为:%.6f' %(end_time - start_time))
return res ##返回函数的返回值
return wrapper
@timeit
def fun3(x):
return x ** 2
a = fun3(3)
print(a)
装饰器问题2:如何保留被装饰函数的函数名和帮助信息文档
-
@functools.wraps(fun)
添加这个东西 -
print(con_add.doc)
打印帮助文档如果没有帮助文档,则返回None -
print(con_add.name)
打印函数名 -
错误做法:
def timeit(fun):
#@functools.wraps(fun) ##如果不添加返回的帮助文档和信息都是wrapper的函数名和信息文档
def wrapper(*args,**kwargs):
'''这是一个wrapper'''
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('系统的运行时间为:%.6f' %(end_time - start_time))
return res
return wrapper
@timeit
def fun3(x):
'''这是一个func3'''
return x ** 2
print(fun3.__doc__)
print(fun3.__name__)
结果:
这是一个wrapper
wrapper
- 正确做法:
def timeit(fun):
@functools.wraps(fun) ##为了保留被修饰的函数名和帮助信息文档
def wrapper(*args,**kwargs):
'''这是一个wrapper'''
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print('系统的运行时间为:%.6f' %(end_time - start_time))
return res
return wrapper
@timeit
def fun3(x):
'''这是一个func3''' ##如果没有帮助文档(函数的注释),则返回None
return x ** 2
print(fun3.__doc__)
print(fun3.__name__)
结果:
这是一个func3
fun3
多个装饰器的执行顺序
- 以下我们用一个例子查看多个装饰器的执行顺序
def decorator_a(func):
print('Get in decorator_a')
def inner_a(*args,**kwargs):
print('Get in inner_a')
res = func(*args,**kwargs) #返回值在两个定义的函数中都要定义
return res
return inner_a
def decorator_b(func):
print('Get in decorator_b')
def inner_b(*args,**kwargs):
print('Get in inner_b')
res = func(*args,**kwargs) #返回值在两个定义的函数中都要定义,且这个返回值的结果在最后执行
return res
return inner_b
@decorator_b
@decorator_a
def f(x):
print('Get in f')
return x * 2
print(f(1))
结果:
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f
2
- 例题:
定义装饰器log,用户判断用户是否登陆成功
定义装饰器isroot,用于判断用户是不是为root
编写这2个装饰器:先判断是否登陆成功,再判断是不是root用户
import functools
import inspect
def is_root(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
#inspect.getcallargs返回值是字典,key值为:形参,value值为:形参对应实参
inspect_res = inspect.getcallargs(fun,*args,**kwargs)
print('inspect_res的返回值为:%s' %inspect_res)
if inspect_res.get('name') == 'root':
print('is root')
res = fun(*args,**kwargs)
return res
else:
print('not root user')
return wrapper
li = ['root','westos','redhat']
def log(fun):
@functools.wraps(fun)
def inlog(name):
if name in li:
print('登陆成功')
res = fun(name)
return res
else:
print('登陆失败')
return inlog
@log
@is_root
def add_student(name): ##此处的name为inspect.getcallargs返回字典中的key值
print('添加学生信息...')
add_student('root')
结果:
登陆成功 ##先判断登陆
inspect_res的返回值为:{'name': 'root'} ##判断是不是root
is root
添加学生信息... ##最后执行返回值