装饰器
装饰器就是对被装饰的对象(函数、类)进行重构的,其可以在不改变原来对象的情况下调用对象时执行重构后的行为
- 解决问题: 在函数执行之前和执行之后添加功能, 调用函数的方式改变了.
- 不改变原有函数的调用方法: 函数里面嵌套函数, 并且返回嵌套的函数
def desc(fun): # fun = login 需要传递一个函数, 要装饰的函数;装饰器函数里面嵌套函数
def add_info():
print('中秋快乐')
fun()
print('欢迎下次光临')
return add_info
def login():
print('loging...')
login=desc(login)
login()
装饰器语法糖
在Python中,可以使用”@”语法糖来精简装饰器的代码,把 decorator 置于函数的定义处,免去给函数重新赋值**(** 即function = decorator(funtion))
如下解释:
import functools #functools标准库
import time #time 时间函数
def add_log(fun):
@functools.wraps(fun) #不加这个的话add模块中__name__,__doc__可能会丢失
def wrapper(*args,**kwargs):
"""*args为元组,**kwargs为字典"""
start_time=time.time()
res=fun(*args,**kwargs)
end_time=time.time()
print('[%s]函数名:%s,运行时间:%.5f,运行返回值:%d' %(time.ctime(),fun.__name__,end_time-start_time,res))
return res #返回值为所要装饰的函数
return wrapper #返回值为所要修饰函数添加的模块
@add_log #调用语法糖
def add(x,y):
time.sleep(0.2)
return x+y
print(add(3,4))
装饰器需求: 获取每个函数的执行时间
- 1).函数执行之前计算时间
- 2).函数执行之后计算时间
eg1:
看字符串拼接的效率
1). “hello” + “world”
2). " ".join([‘hello’, “world”])
import random
import string
import time
li=[random.choice(string.ascii_letters) for i in range(30)]
def timeit(fun):
def wrapper(*args,**kwargs):
#函数执行之前
start_time=time.time()
#执行函数
fun()
#执行函数之后
end_time=time.time()
print('运行时间:%.6f' %(end_time-start_time))
return wrapper
@timeit
def con_add():
s=''
for i in li:
s+=(i+',')
print(s)
@timeit
def join_add():
print(','.join(li))
con_add()
join_add()
eg2:
检测列表生成式和map的效率高低, n为函数传入的参数===两者实力相当
1). [n2 for i in range(n)]
2). map(lambda x:x2, range(n))
import time
def timeit(fun):
def wrapper(*args,**kwargs):
"""这是一个wrapper函数"""
start_time=time.time()
fun(*args,**kwargs)
end_time=time.time()
print('运行时间为:%.6f' %(end_time-start_time))
return wrapper
@timeit
def fun_list(n):
print([2*i for i in range(n)])
@timeit
def fun_map(n):
print(list(map(lambda x:x*2,range(n))))
fun_list(50000)
fun_map(50000)
print(fun_list.__name__)
print(fun_list.__doc__)
代码中最后两行,输出的是fun_list信息,但是打印出来却是关于装饰器的信息,那么如何来保留被装饰函数的信息呢?
保留被装饰的函数的函数名和帮助文档信息
解决办法:导入functools标准库,使用functools.wraps函数,保留被装饰的函数的函数名和帮助文档信息。
import functools
import time
def timeit(fun):
"""这是一个装饰器timeit"""
@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 fun_list(n):
"""这是一个fun_list函数,被timeit装饰"""
return [2*i for i in range(n)]
@timeit
def fun_map(n):
"""这是一个fun_map函数"""
return map(lambda x:x*2,range(n))
fun_list(5000)
fun_map(5000)
print(fun_list.__name__)
print(fun_list.__doc__)
装饰器中不同条件下执行不同的函数
要求: 用户登陆验证的装饰器is_login
1). 如果用户登陆成功, 则执行被装饰的函数;
2). 如果用户登陆不成功, 则执行登陆函数
import functools
login_users=['admin','root']
def is_login(fun):#fun: writeBlog
@functools.wraps(fun)
def wrapper(*args,**kwargs):
#判断博客的用户是否登陆成功
if kwargs.get('name') in login_users:
res=fun(*args,**kwargs)
return res
else:
res=login()
return res
return wrapper
#必须登陆成功
@is_login
def writeBlog(name):
return '写博客'
def login():
return '登陆...'
#是否登陆成功都可以执行的代码
def news():
print('开始播放新闻...')
print(writeBlog(name='admin'))
必须登陆成功的执行结果
用户不在登陆名单执行结果
装饰器日志
1.创建add_log装饰器, 被装饰的函数打印日志信息;
2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
import functools
import time
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time=time.time()
res=fun(*args,**kwargs)
end_time=time.time()
print('[%s] 函数名:%s,运行时间:%.5f,运行返回值:%d' %(time.ctime(),fun.__name__,end_time-start_time,res))
return res
return wrapper
@add_log
def add(x,y):
time.sleep(0.2)
return x+y
print(add(4,5))
带有多个装饰器的函数
def makebold(fun):
print('bold1')
def wrapper1(*args,**kwargs):
print('bold2')
return fun(*args,**kwargs)
return wrapper1
def makei(fun):
print('i1')
def wrapper(*args,**kwargs):
print('i2')
return fun(*args,**kwargs)
return wrapper
#当有多个装饰器时,从下到上调用装饰器,
#真实wrapper内容执行是从上到下执行.
@makebold #login=makebold(loogin) login为wrapper1
@makei #login=nakei(login) login为wrapper
def login():
return 'login...'
print(login())
练习:判断登陆用户是否为管理员is_admin(此处管理员只有一个为:admin用户)
1).如果用户为管理员, 则执行被装饰的函数;
2).如果用户不是管理员, 则报错;
import functools
login_users=['admin','root']
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
if kwargs.get('name')=='admin':
res=fun(*args,**kwargs)
return res
else:
return 'Error 没有访问权限'
return wrapper
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
if kwargs.get('name') in login_users:
res=fun(*args,**kwargs)
return res
else:
res=login()
return res
return wrapper
#必须登陆成功
@is_login
@is_admin
def writeBlog(name):
return '编写博客'
def login():
return '登陆...'
#无论登陆成功都执行的代码
def news():
print('this is a news')
print(writeBlog(name='root'))
管理员为root执行的结果
管理员为admin执行的结果
带有参数的装饰器
创建装饰器, 要求如下:
1.创建add_log装饰器, 被装饰的函数打印日志信息;
2.日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
import functools
import time
def log(kind):
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
start_time=time.time()
res=fun(*args,**kwargs)
end_time=time.time()
print('<%s> [%s] 函数名:%s,运行时间:%.6f,返回值:%d' %(kind,time.ctime(),fun.__name__,end_time-start_time,res))
return res
return wrapper
return add_log
@log('debug') #log('debug')===>返回值是add_log #add=add_log(add)
def add(x,y):
time.sleep(0.3)
return x+y
print(add(3,5))
装饰器判断变量类型
编写装饰器required_ints, 条件如下:
1). 确保函数接收到的每一个参数都是整数;
2). 如果参数不是整形数, 打印 TypeError:参数必须为整形
import functools
def required_ints(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
for i in args:
if not isinstance(i,int):
print('函数所有参数并非为整型')
break
else:
res=fun(*args,**kwargs)
return res
return wrapper
@required_ints
def add(a,b):
return a+b
@required_ints
def myMax(a,b,c,d):
return max(a,b,c,d)
print(add(2,4.4))
print(myMax(5,8,3,9.2))
整型结果;