装饰器
什么是装饰器:
'装饰’代指为被装饰对象添加新的功能,’器’代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
提示:可调用对象有函数,方法或者类,此处我们单以本章主题函数为例,来介绍函数装饰器,并且被装饰的对象也是函数。
开发封闭原则
对扩展开放,对修改封闭。
装饰器的原则:
1、不能修改被装饰函数的源代码
2、不能修改被装饰函数的调用方式
装饰器的目的:
在遵循装饰器原则的情况下为被装饰对象加上新功能
装饰器的调用方式:
方式一:
def print_info(func): #func=被装饰对象的函数名 def inner(): print("程序开始执行") func() #被装饰对象的原功能 print("程序执行完成") return inner def auth(): print("程序正在执行") auth=print_info(auth) #此时的auth就是inner函数的内存地址,而括号内的auth是原始auth函数 auth() #此时就是在调用inner函数
方式二:在被装饰对象正上方加上@装饰器名(语法糖@)
def print_info(func): #func=被装饰对象的函数名 def inner(): print("程序开始执行") func() #被装饰对象的原功能 print("程序执行完成") return inner @print_info #等于auth=print_info(auth) def auth(): print("程序正在执行")
装饰器的实现:
函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。
无参装饰器:
被修饰对象没有参数
def print_info(func): #func=被装饰对象的函数名 def inner(): print("程序开始执行") func() #被装饰对象的原功能 print("程序执行完成") return inner @print_info def auth(): print("程序正在执行") auth() #此时就是在调用inner函数
被装饰对象有参数:
def print_info(func): #func=被装饰对象的函数名 def inner(*args,**kwargs): print("程序开始执行") func(*args,**kwargs) #被装饰对象的原功能 print("程序执行完成") return inner @print_info def auth(name): print("程序正在执行") print("欢迎{}用户".format(name)) auth(name="egon") #此时就是在调用inner函数
被装饰函数有返回值:
def print_info(func): #func=被装饰对象的函数名 def inner(*args,**kwargs): print("程序开始执行") res=func(*args,**kwargs) #被装饰对象的原功能 print("程序执行完成") return res return inner @print_info def auth(name): print("程序正在执行") print("欢迎{}用户".format(name)) return 111 res=auth(name="egon") #此时就是在调用inner函数 print(res)
无参装饰器模板:
def print_info(func): #func=被装饰对象的函数名 def inner(*args,**kwargs): print("程序开始执行") res=func(*args,**kwargs) #被装饰对象的原功能 print("程序执行完成") return res return inner
有参装饰器:
import time def auth(engine="file"): def auth2(func): def inner(*args,**kwargs): if engine=="file": name=input("请输入用户名:>>>").strip() passwd=input("请输入密码:>>>").strip() if name == "egon" and passwd == "123": print("login successful") return func(*args,**kwargs) else: print("login faild") elif engine=="mysql": print("mysql auth") elif engine=="ldap": print("ldap auth") else: print("engin not exists") return inner return auth2 @auth(engine="file") #等于auth2,然后index=auth2(index) def index(name): time.sleep(1) print("welecome %s to index" %name) return 111 res=index("egon") print(res)
wraps使用:(用来将装饰器伪装成被装饰函数使之更像,比如一些函数注释等等信息,使用前后都和被装饰函数一样,用户感知不到变化
)
from functools import wraps #导入wraps模块,wraps本质也是函数 import time def timmer(func): @wraps(func) #用于将index的help信息同化给inner def inner(*args,**kwargs): start=time.time() func(*args,**kwargs) end=time.time() print("程序执行时间{:.2f}".format(end-start)) return inner @timmer #==》index=timmer(index)==>inner函数 def index(name): """index 函数 help...""" time.sleep(1) print("welecome %s to index" %name) return 111 print(help(index)) #查看函数信息(查看的是inner的帮助信息)
叠加多个装饰器的加载、运行分析
def deco1(func1): # func1 = wrapper2的内存地址 def wrapper1(*args,**kwargs): print('正在运行===>deco1.wrapper1') res1=func1(*args,**kwargs) print('执行结束===>deco1.wrapper1') return res1 return wrapper1 def deco2(func2): # func2 = wrapper3的内存地址 def wrapper2(*args,**kwargs): print('正在运行===>deco2.wrapper2') res2=func2(*args,**kwargs) print('执行结束===>deco2.wrapper2') return res2 return wrapper2 def deco3(x): def outter3(func3): # func3=被装饰对象index函数的内存地址 def wrapper3(*args,**kwargs): print('正在运行===>deco3.outter3.wrapper3') res3=func3(*args,**kwargs) print('执行结束===>deco3.outter3.wrapper3') 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)