装饰器
一:装饰器简介
1.1 什么是装饰器:
'装饰'代指为被装饰对象添加新的功能,'器'代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。
1.2 为什么要用装饰器:
软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。
软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。
1.3 语法糖:
在Python语法中,为了更简洁而优雅的使用装饰器,专门提供了一种语法来取代index=timer(index)的形式,需要在被装饰对象的正上方单独一行添加@timer,当解释器解释到@timer时就会调用timer函数,且把它正下方的函数名当做实参传入,然后将返回的结果重新赋值给原函数名
@timer # index=timer(index) def index(): time.sleep(3) print('Welcome to the index page') return 200
如果在一个函数中需要使用多个装饰器,可以叠加调用,叠加多个装饰器也无特殊之处,加载顺序自上而下,执行顺序自上而下,下面会详细说明
装饰器的实现原理有一些类似于偷梁换柱的概念,即将原函数名指向的内存地址偷梁换柱成warpper函数,因此应该将warpper做的和原函数一样才行
二:装饰器的实现
函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,都是’函数嵌套+闭包+函数对象’的组合使用的产物。
2.1 无参装饰器
def outter(func):
def wrapper(*args,**kwargs):
# 1、调用原函数
# 2、为其增加新功能
res=func(*args,**kwargs)
return res
return wrapper
2.2 有参装饰器
由于语法糖@的限制,outter函数只能有一个参数,并且该参数只用来接收被装饰对象的内存地址
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
可以使用help来查看函数的文档注释,本质就是查看函数的doc属性,但是对于被装饰之后的函数,查看文档注释
print(help(home))
打印结果: Help on function wrapper in module __main__: wrapper(*args, **kwargs) None
2.3 装饰器的修正
若想要保留原函数的文档和函数名属性,需要修正装饰器 def timer(func): def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res wrapper.__doc__=func.__doc__ wrapper.__name__=func.__name__ return wrapper 手动将原函数的属性赋值给warpper函数: 1、函数wrapper.__name__ = 原函数.__name__ 2、函数wrapper.__doc__ = 原函数.__doc__ wrapper.__name__ = func.__name__ wrapper.__doc__ = func.__doc__ Python中有一个专门的模块functools下提供了一个装饰器warps专门用来帮我们实现这件事 from functools import wraps def timer(func): @wraps(func) def wrapper(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('run time is %s' %(stop_time-start_time)) return res return wrapper
2.4 装饰器的叠加
类装饰器: 无参类装饰器: class Kuozhan(): def __call__(self, func): # 把对象当作函数调用的时候自动触发 return self.kuozhan2(func) def kuozhan1(func): def newfunc(): print("考试前,认真复习") func() print("考试后,咋咋呼呼") return newfunc def kuozhan2(self,func): def newfunc(): print("考试前,开开心心") func() print("考试后,又哭又闹") return newfunc @Kuozhan.kuozhan1 def func(): print("考试中......") func() print(">>>>>>>>>>>>>>>>>") @Kuozhan() def func(): print("考试中......") func() 有参类装饰器: class Kuozhan(): money = "故宫门票,每人100一次," def __init__(self,num): self.num = num def __call__(self, cls): if self.num == 1: return self.newfunc1(cls) elif self.num == 2: return self.newfunc2(cls) def ad(self): print("故宫一般指北京故宫。北京故宫是中国明清两代的皇家宫殿,旧称紫禁城,位于北京中轴线的中心。北京故宫以三大殿为中心....") def newfunc1(self,cls): def newfunc(): cls.money = Kuozhan.money cls.ad = Kuozhan.ad return cls() return newfunc def newfunc2(self,cls): def newfunc(): if "run" in cls.__dict__: res = cls.run() cls.run = res return cls() return newfunc @Kuozhan(1) class MyClass(): def run(): return "程序运行成功。。。" obj = MyClass() print(obj.money) obj.ad() @Kuozhan(2) class MyClass(): def run(): return "程序运行成功。。。" obj = MyClass() print(obj.run)