1、什么是装饰器
器指的是工具,可以定义成成函数
装饰指的是为其他事物添加额外的东西点缀
合到一起的解释:装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
2、为何要用装饰器
开放封闭原则
开放:指的是对拓展功能是开放的
封闭:指的是对修改源代码是封闭的
装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能
3、如何用
1.无参装饰器
需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
让我们一步步的得到装饰器
def index(x,y): time.sleep(3) print('index %s %s' %(x,y)) index(111,222)
解决方案一失败:没有修改被装饰对象的调用方式,但是修改了其源代码
import time def index(x,y): start=time.time() time.sleep(3) print('index %s %s' %(x,y)) stop = time.time() print(stop - start) index(111,222)
解决方案二失败:没有修改被装饰对象的调用方式,也没有修改了其源代码,并且加上了新功能,但是代码冗余
import time def index(x,y): time.sleep(3) print('index %s %s' %(x,y)) start=time.time() index(111,222) stop=time.time() print(stop - start)
解决方案三失败:解决了方案二代码冗余问题,但带来一个新问题即函数的调用方式改变了
import time def index(x,y): time.sleep(3) print('index %s %s' %(x,y)) def wrapper(): start=time.time() index(111,222) stop=time.time() print(stop - start) wrapper()
大方向:如何在方案三的基础上不改变函数的调用方式
方案三优化一:将index的参数写活了
import time def index(x,y): time.sleep(3) print('index %s %s ' %(x,y)) def wrapper(*args,**kwargs): start=time.time() index(*args,**kwargs) stop=time.time() print(stop - start) wrapper(3333,4444)
方案三优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index
import time def index(x,y): time.sleep(3) print('index %s %s %s' %(x,y)) def home(name): time.sleep(2) print('welcome %s to home page' %name) def outter(func): def wrapper(*args,**kwargs): start=time.time() func(*args,**kwargs) stop=time.time() print(stop - start) return wrapper index=outter(index) # index=wrapper的内存地址 home=outter(home) # home=wrapper的内存地址 home('egon')
方案三优化三:将wrapper做的跟被装饰对象一模一样,以假乱真
import time def home(name): time.sleep(2) print('welcome %s to home'%name) return 123 def index(x,y): time.sleep(2) print('index %s %s' % (x, y)) def outtor(func): def wrapper(*args,**kwargs): start = time.time() res = func(*args,**kwargs) #返回值也要一样 stop = time.time() print(stop-start) return res return wrapper index = outtor(index)# 偷梁换柱:index这个名字指向的wrapper函数的内存地址 index(1,2) home = outtor(home)# 偷梁换柱:home这个名字指向的wrapper函数的内存地址 res = home('jjj') print(res)
最终结果:
语法糖:在被装饰对象正上方的单独一行写@装饰器名字(让你开心的语法
import time @outtor def home(name): time.sleep(2) print('welcome %s to home'%name) return 123 @outtor def index(x,y): time.sleep(2) print('index %s %s' % (x, y)) def outtor(func): def wrapper(*args,**kwargs): start = time.time() res = func(*args,**kwargs) #返回值也要一样 stop = time.time() print(stop-start) return res return wrapper index(1,2) res = home('jjj') print(res)
2.有参装饰器
由于语法糖@的限制,outter函数只能有一个参数,并且该参数只用来接收被装饰对象的内存地址,而wrapper的参数同样不能修改
def auth(db_type): def deco(func): def wrapper(*args, **kwargs): name = input('your name>>>: ').strip() pwd = input('your password>>>: ').strip() if db_type == 'file': print('基于文件的验证') if name == 'egon' and pwd == '123': res = func(*args, **kwargs) return res else: print('user or password error') elif db_type == 'mysql': print('基于mysql的验证') elif db_type == 'ldap': print('基于ldap的验证') else: print('不支持该db_type') return wrapper return deco @auth(db_type='file') # @deco # index=deco(index) # index=wrapper def index(x, y): print('index->>%s:%s' % (x, y)) @auth(db_type='mysql') # @deco # home=deco(home) # home=wrapper def home(name): print('home->>%s' % name) @auth(db_type='ldap') # 账号密码的来源是ldap def transfer(): print('transfer')
# 有参装饰器模板 def 有参装饰器(x,y,z): def outter(func): def wrapper(*args, **kwargs): res = func(*args, **kwargs) return res return wrapper return outter @有参装饰器(1,y=2,z=3) def 被装饰对象(): pass
3.装饰器的补充
通过from functools import wraps并在wraps装饰器上方加上@wraps(func)可以让wrapper做的跟原函数一样才行
叠加多个装饰器的加载、运行分析(了解***)
def deco1(func1): # func1 = wrapper2的内存地址 def wrapper1(*args,**kwargs): print('正在运行===>deco1.wrapper1') res1=func1(*args,**kwargs) return res1 return wrapper1 def deco2(func2): # func2 = wrapper3的内存地址 def wrapper2(*args,**kwargs): print('正在运行===>deco2.wrapper2') res2=func2(*args,**kwargs) return res2 return wrapper2 def deco3(x): def outter3(func3): # func3=被装饰对象index函数的内存地址 def wrapper3(*args,**kwargs): print('正在运行===>deco3.outter3.wrapper3') res3=func3(*args,**kwargs) return res3 return wrapper3 return outter3 # 加载顺序自下而上(了解) @deco1 # index=deco1(wrapper2的内存地址) ===> index=wrapper1的内存地址 @deco2 # index=deco2(wrapper3的内存地址) ===> index=wrapper2的内存地址 @deco3(111) # ===>@outter3===> index=outter3(index) ===> index=wrapper3的内存地址 def index(x,y): print('from index %s:%s' %(x,y)) # 执行顺序自上而下的,即wraper1-》wrapper2-》wrapper3 index(1,2) # wrapper1(1,2)