装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:
(1).不能修改被装饰的函数的源代码
(2).不能修改被装饰的函数的调用方式
(3).满足(1)、(2)的情况下给程序增添功能
实现:
我们写一个嵌套函数,在内部函数中添加新功能新内容,然后调用原函数,再在外部函数return这个内部函数。由于函数也是一个对象,而且函数对象可以被赋值给变量,所以我们调用这个嵌套函数,把返回值(返回的是一个函数)赋给一个和原函数同名的变量,通过变量也能调用该函数。这样就实现了不改变原函数代码增强其功能。
创新互联网站建设提供从项目策划、软件开发,软件安全维护、网站优化(SEO)、网站分析、效果评估等整套的建站服务,主营业务为做网站、网站制作,成都app软件开发以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。创新互联深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
举个例子:
已经有一个函数login()实现的登陆功能,我想给它添加一些新内容:
def desc(fun):
def add_info():
print('写在前面.......')
fun()
print('写在后面........')
return add_info
def login():
print('login............')
login = desc(login)
login()
运行:
其实这就是一个装饰器的雏形了。但是在使用这个装饰器装饰的时候,需要在每个函数前面加上这样一句代码:
function = desc(function)
显然有些麻烦,Python提供了一种语法糖来代替它:
@装饰器名 #注意要把这句代码放到原函数前
代码修改如下,会时同样的效果且更加简洁:
def desc(fun):
def add_info():
print('写在前面.......')
fun()
print('写在后面........')
return add_info
@desc
def login():
print('login............')
login()
用装饰器来实现计时功能:
计算在字符串连接时,+和join那个效率更高。
import random,string,time
li = [random.choice(string.ascii_letters) for i in range(1000)] #这里用了1000个字符来比较
def desc(fun):
def wrapper():
start_time = time.time()
fun()
end_time = time.time()
print('%.8f' %(end_time-start_time))
return wrapper
@desc
def a_add():
st = ''
for i in li:
st += (i+',')
return st
@desc
def b_add():
return ','.join(li)
a_add()
b_add()
运行:
显然,此时join方式效率要高于连接符+
可以看到以上内容中函数没有参数,即是无参函数装饰器。
用装饰器来实现判断变量类型:
对函数的传入参数进行检查,参数符合要求(整数)正常调用函数,不符合要求则报错。
import functools #导入functools包
def required_int(fun):
@functools.wraps(fun) #functools包中的wraps函数保证原函数的信息不因装饰器而改变。
def wrapper(*args): #可变参数
for i in args:
if not str(i).isdigit(): #或者用isinstance(i,int)更加简洁
print('参数必须是整数...')
return None
break
else:
res=fun(*args)
return res
return wrapper
@required_int
def add_num(*args): #参数为可变参数
sum = 0
for i in args:
sum += i
return sum
print('参数合法输出结果:')
print(add_num(2,3,45,6))
print('参数不合法输出结果:')
print(add_num(3,4,5,'e'))
运行:
模仿博客逻辑功能:
登陆后才能写博客,浏览博客不需要登陆。
import functools
login_users = ['admin','root'] #已经登陆的用户
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs): #可变参数加关键字参数
if kwargs.get('name') in login_users:
res = fun(*args,**kwargs)
else:
res = login()
return res
return wrapper
def login():
return '请先登陆.......'
@is_login
def writtelog(name):
return '写博客日之.....'
def viewnews():
return '看文章新闻.....'
print(writtelog(name='root'))
print(writtelog(name='root11'))
print(viewnews())
运行:
模仿只有admin用户在登陆后才能进入后台:
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)
else:
res = '没有权限....'
return res
return wrapper
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
if kwargs.get('name') in login_users:
res = fun(*args,**kwargs)
else:
res = login()
return res
return wrapper
def login():
return '请先登陆.......'
@is_login
@is_admin
def houtai(name):
return '进入后台.....'
print(houtai(name='admin'))
print(houtai(name='root'))
print(houtai(name='uuuu'))
运行:
这里需要注意的是,能用一个装饰器尽量不要使用多个装饰器。
当有多个装饰器时,从上往下依次调用装饰器,真实的wrapper内容时从上到下执行的。
一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。
将参数传入,我们需要再加一层函数嵌套,来传递装饰器的参数。
举栗子:
对两个不同的函数计时:
import time
def timer(parameter): #参数parameter传入调用的函数的名字
def outer_wrapper(func):
def wrapper(*args, **kwargs):
if parameter == 'task1':
start_time = time.time()
func(*args, **kwargs)
stop_time = time.time()
print("the task1 run time is :", stop_time - start_time)
elif parameter == 'task2':
start_time = time.time()
func(*args, **kwargs)
stop_time = time.time()
print("the task2 run time is :", stop_time - start_time)
return wrapper
return outer_wrapper
@timer(parameter='task1')
def task1():
time.sleep(2)
print("in the task1")
@timer(parameter='task2')
def task2():
time.sleep(2)
print("in the task2")
task1()
task2()
运行:
判断函数传入参数升级版本:
import functools,math
def required(*argss):
def required_01(fun):
@functools.wraps(fun)
def wrapper(*args):
for i in args:
if not isinstance(i,argss): #或者用isinstance(i,int)
print('参数必须是:',*argss)
return None
break
else:
res=fun(*args)
return res
return wrapper
return required_01
@required(str,int) #这里可以看到要求传入参数时str或者int
def add_num(*args):
return args
print(add_num('ssss',789)) #符合要求
print(add_num('aaaa',693,3.14)) #有浮点型,不符合要求
运行: