装饰器的推演过程

python学习17——装饰器之无参装饰器&语法糖的定义

 

一.什么是装饰器

’装饰’代指为被装饰对象添加新的功能,’器’代指器具/工具,装饰器与被装饰的对象均可以是任意可调用对象。概括地讲,装饰器的作用就是在不修改被装饰对象源代码和调用方式的前提下为被装饰对象添加额外的功能。装饰器经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

简言之,装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能

二.为什么要用装饰器

软件的设计应该遵循开放封闭原则,即对扩展是开放的,而对修改是封闭的。对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着对象一旦设计完成,就可以独立完成其工作,而不要对其进行修改。

软件包含的所有功能的源代码以及调用方式,都应该避免修改,否则一旦改错,则极有可能产生连锁反应,最终导致程序崩溃,而对于上线后的软件,新需求或者变化又层出不穷,我们必须为程序提供扩展的可能性,这就用到了装饰器。

三.如何用装饰器

需求:在不修改index函数的源代码以及调用方式的前提下为其添加统计运行时间的功能
def index(x,y):
time.sleep(3)
print('index %s %s' %(x,y))

index(111,222)
# index(y=111,x=222)
# index(111,y=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)



start=time.time()
index(111,222)
stop=time.time()
print(stop - start)


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,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))

def wrapper(*args,**kwargs):
start=time.time()
index(*args,**kwargs) # index(3333,z=5555,y=44444)
stop=time.time()
print(stop - start)

# wrapper(3333,4444,5555)
# wrapper(3333,z=5555,y=44444)


方案三的优化二:在优化一的基础上把被装饰对象写活了,原来只能装饰index
import time

def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))

def home(name):
time.sleep(2)
print('welcome %s to home page' %name)


def outter(func):
# func = index的内存地址
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs) # index的内存地址()
stop=time.time()
print(stop - start)
return wrapper

index=outter(index) # index=wrapper的内存地址
home=outter(home) # home=wrapper的内存地址


home('egon')
# home(name='egon')

方案三的优化三:将wrapper做的跟被装饰对象一模一样,以假乱真
import time

def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))

def home(name):
time.sleep(2)
print('welcome %s to home page' %name)

def outter(func):
def wrapper(*args,**kwargs):
start=time.time()
res=func(*args,**kwargs)
stop=time.time()
print(stop - start)
return res

return wrapper
# 偷梁换柱:home这个名字指向的wrapper函数的内存地址
home=outter(home)
res=home('egon') # res=wrapper('egon')
print('返回值--》',res)

四.语法糖
在被装饰对象正上方的单独一行写@装饰器名字
@timmer # index=timmer(index)
def index(x,y,z):
time.sleep(3)
print('index %s %s %s' %(x,y,z))

@timmer # home=timmer(ome)
def home(name):
time.sleep(2)
print('welcome %s to home page' %name)

# index(x=1,y=2,z=3)
# home('egon')

 

# 思考题(选做),叠加多个装饰器,加载顺序与运行顺序
# @deco1 # index=deco1(deco2.wrapper的内存地址)
# @deco2 # deco2.wrapper的内存地址=deco2(deco3.wrapper的内存地址)
# @deco3 # deco3.wrapper的内存地址=deco3(index)
# def index():
# pass

 

# 总结无参装饰器模板
# def outter(func):
# def wrapper(*args,**kwargs):
# # 1、调用原函数
# # 2、为其增加新功能
# res=func(*args,**kwargs)
# return res
# return wrapper

 

 

 


def auth(func):
def wrapper(*args,**kwargs):
# 1、调用原函数
# 2、为其增加新功能
name=input('your name>>: ').strip()
pwd=input('your password>>: ').strip()
if name == 'egon' and pwd == '123':
res=func(*args,**kwargs)
return res
else:
print('账号密码错误')
return wrapper

 

@auth
def index():
print('from index')

index()

posted @ 2021-03-16 09:57  欧阳锦涛  阅读(27)  评论(0)    收藏  举报
TOP 底部