Python学习—装饰器
装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:
(1).不能修改被装饰的函数的源代码
(2).不能修改被装饰的函数的调用方式
(3).满足(1)、(2)的情况下给程序增添功能
实现:
我们写一个嵌套函数,在内部函数中添加新功能新内容,然后调用原函数,再在外部函数return这个内部函数。由于函数也是一个对象,而且函数对象可以被赋值给变量,所以我们调用这个嵌套函数,把返回值(返回的是一个函数)赋给一个和原函数同名的变量,通过变量也能调用该函数。这样就实现了不改变原函数代码增强其功能。
举个例子:
已经有一个函数login()实现的登陆功能,我想给它添加一些新内容:
def desc(fun): def add_info(): print('写在前面.......') fun() print('写在后面........') return add_infodef login(): print('login............')login = desc(login)login()
运行:
其实这就是一个装饰器的雏形了。但是在使用这个装饰器装饰的时候,需要在每个函数前面加上这样一句代码:
function = desc(function)
显然有些麻烦,Python提供了一种语法糖来代替它:
@装饰器名 #注意要把这句代码放到原函数前
代码修改如下,会时同样的效果且更加简洁:
def desc(fun): def add_info(): print('写在前面.......') fun() print('写在后面........') return add_info@descdef login(): print('login............')login()
用装饰器来实现计时功能:
计算在字符串连接时,+和join那个效率更高。
import random,string,timeli = [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@descdef a_add(): st = '' for i in li: st += (i+',') return st@descdef 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_intdef add_num(*args): #参数为可变参数 sum = 0 for i in args: sum += i return sumprint('参数合法输出结果:')print(add_num(2,3,45,6))print('参数不合法输出结果:')print(add_num(3,4,5,'e'))
运行:
模仿博客逻辑功能:
登陆后才能写博客,浏览博客不需要登陆。
import functoolslogin_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 wrapperdef login(): return '请先登陆.......'@is_logindef writtelog(name): return '写博客日之.....'def viewnews(): return '看文章新闻.....'print(writtelog(name='root'))print(writtelog(name='root11'))print(viewnews())
运行:
模仿只有admin用户在登陆后才能进入后台:
import functoolslogin_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 wrapperdef 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 wrapperdef login(): return '请先登陆.......'@is_login@is_admindef houtai(name): return '进入后台.....'print(houtai(name='admin'))print(houtai(name='root'))print(houtai(name='uuuu'))
运行:
这里需要注意的是,能用一个装饰器尽量不要使用多个装饰器。
当有多个装饰器时,从上往下依次调用装饰器,真实的wrapper内容时从上到下执行的。
一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。
将参数传入,我们需要再加一层函数嵌套,来传递装饰器的参数。
举栗子:
对两个不同的函数计时:
import timedef 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,mathdef 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或者intdef add_num(*args): return argsprint(add_num('ssss',789)) #符合要求print(add_num('aaaa',693,3.14)) #有浮点型,不符合要求
运行:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。