1.定义
装饰器就是用来装饰函数。
想要增强原有函数的功能;
但不希望修改now()函数的定义;
在代码运行期间动态增加功能的方式
2.创建装饰器
def desc(fun): # fun=login 需要传递一个函数,即要装饰的函数
def add_info(): ###装饰器函数里面嵌套函数
print('你好')
fun() ###login()
print('欢迎再光临')
return add_info ##返回值是嵌套函数
@desc
def login():
print('login...')
login()
3.装饰器的应用场景
1)装饰器实现计时器
需求:获取每个函数执行的时间
例1:看字符串拼接的效率
‘hello’+’world’
” “.join([‘hello’,’world’])
例2:查看生成列表的快慢
list
map
import random
import time
import string
li=[random.choice(string.ascii_letters) for i in range(1,100)]
def timeit(fun): # con_add
def wrapper(*args,**kwargs):###接收可变参数和关键字参数
# 函数执行前
start_time = time.time()
# 执行函数
fun(*args,**kwargs)##args解包
# 函数执行后
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)
con_add()
@timeit
def join_add():
print(",".join(li))
join_add()
@timeit
def con_list(n):
print([i*2 for i in range(n)])
con_list(50)
@timeit
def con_map(n):
print (list(map(lambda x:x*2 , range(n))))
con_map(50)
引入问题
1.函数有返回值的解决办法
把执行函数的值赋给一个变量,最后在装饰函数的嵌套函数返回这个值即可
import random
import time
import string
def timeit(fun): # con_add
def wrapper(*args,**kwargs):###接收可变参数和关键字参数
# 函数执行前
start_time = time.time()
# 执行函数
res=fun(*args,**kwargs)##args解包
# 函数执行后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return res
return wrapper
@timeit
def con_list(n):
return ([i*2 for i in range(n)])
print(con_list(50))
@timeit
def con_map(n):
return list(map(lambda x:x*2 , range(n)))
print(con_map(50))
2.保留被装饰函数的函数名和帮助文档信息
这里需要导入funtools模块,且要执行functools.wraps(fun) 命令(当然这个fun根据你的定义写)
import random
import time
import string
import functools
def timeit(fun): # con_add
@functools.wraps(fun) ##保留被装饰函数的函数名和帮助文档信息
def wrapper(*args,**kwargs):###接收可变参数和关键字参数
# 函数执行前
start_time = time.time()
# 执行函数
res=fun(*args,**kwargs)##args解包
# 函数执行后
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return res
return wrapper
@timeit
def con_list(n):
print([i*2 for i in range(n)])
print(con_list(50))
@timeit
def con_map(n):
return list((map(lambda x:x*2 , range(n))))
print(con_map(50))
print(con_list.__name__)
print(con_list.__doc__)
2)查看日志信息
需求:创建add_log装饰器,被装饰的函数打印日志信息;
日志格式为:字符串时间 函数名;运行时间;运行返回值结果
import time
import functools
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
ctime=time.ctime() ###字符串时间
fun_name=fun.__name__
start_time = time.time()
res=fun(*args,**kwargs)
end_time=time.time()
print("[%s] 函数名:%s 运行时间为:%.6f 运行返回结果:%s" % (ctime,fun_name,end_time - start_time,res))
return res
return wrapper
@add_log
def logmessags():
'''日志信息'''
return 'hello'
logmessags()
3)用户登陆验证(装饰器的第二种方式)
如果用户登陆成功,则执行被装饰的函数
如果用户登陆不成功,则执行登陆函数
import functools
login_users=['root','admin']
def is_login(fun): # fun: writeBlog
@functools.wraps(fun)
def wrapper(*args, **kwargs): # name="admin" # kwargs={"name":"admin"}
# 判断写博客的这个用户是否登陆成功
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'))
4.无参数的装饰器
需求:
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):
return 'TypeError: 参数必须为整形'
break
else:
res=fun(*args,**kwargs)
return res
return wrapper
@required_ints
def add(a,b):
return a+b
print(add(1,1.0))
@required_ints
def Max(a,b,c,d):
return max(a,b,c,d)
print(Max(1,2,3,4))
5.含参装饰器
例:对上例的升级版
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxx
import functools
def required_types(*kinds): # kinds
def required(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs): # args=(1,2)
for i in args: # i=1
# if isinstance(i, int):
# pass
# else:
# print("函数所有参数并非为整形")
# break
if not isinstance(i , kinds):
print("函数所有参数并非", kinds)
break
else:
res = fun(*args, **kwargs)
return res
return wrapper
return required
@required_types(int, float)
def add(a, b):
return a + b
@required_types(int)
def myMax(a, b, c, d):
return max(a, b, c, d)
print(add(1,2.0))
例2:
建装饰器,要求如下:
日志格式为:日志等级:字符串时间 函数名;运行时间;运行返回值结果
import time
import functools
def log(kind): ##kind='debug'
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
ctime=time.ctime()
fun_name=fun.__name__
start_time = time.time()
res=fun(*args,**kwargs)
end_time=time.time()
print("<%s> [%s] 函数名:%s 运行时间为:%.6f 运行返回结果:%s" % (kind,ctime,fun_name,end_time - start_time,res))
return res
return wrapper
return add_log
@log('debug')
##log('debug')==>返回值是add_log
##messages=add_log(messages)
def logmessags():
'''日志信息'''
return 'hello'
logmessags()
有无参数的区别在于装饰的函数后跟不跟参数
@required_ints
def add(a,b):
return a+b
print(add(1,1.0))
@log('debug')
def logmessags():
'''日志信息'''
return 'hello'
6.含有多个装饰器
##当有多个装饰器时,从下到上调用装饰器
##真实wrapper内容执行时从上到下执行
import functools
def makebold(fun):
print("bold1")
def wrapper1(*args, **kwargs):
print("bold2")
return fun(*args, **kwargs) # wrapper
return wrapper1
def makei(fun): # fun=login
print("i1")
def wrapper(*args, **kwargs):
print("i2")
return fun(*args, **kwargs)
return wrapper
##当有多个装饰器时,从下到上调用装饰器
##真实wrapper内容执行时从上到下执行
@makebold
@makei
def login():
return '登陆'
print(login())
例;需求:用户登录验证的装饰器
1)如果用户登陆成功,则执行被装饰的函数
2)如果用户登陆不成功,则执行登陆函数
2.需求:判断登陆的用户是否为管理员(此处管理员只有一个)
1)如果用户为管理员,则执行被装饰的函数
2)如果用户不是管理员,则报错
import functools
login_users=['root','admin']
def is_admin(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): # fun: writeBlog
@functools.wraps(fun)
def wrapper(*args, **kwargs): # name="admin" # kwargs={"name":"admin"}
# 判断写博客的这个用户是否登陆成功
if kwargs.get("name") in login_users:
res = fun(*args, **kwargs)
return res
else:
res=login()
return res
return wrapper
@is_admin
@is_login
# 必须登陆成功
def writeBlog(name):
return '写博客'
def login():
return '你要登陆阿'
# 是否登陆成功均可进入
def news():
print('新闻/。。。')
print(writeBlog(name='admin'))