今日内容
装饰器
什么是装饰器
装饰器是一个用来为被装饰者添加功能的工具
被装饰者:函数
装饰器:可以用函数实现装饰器这种工具
为何要用装饰器
不影响源代码,调用方式不改变,同时增加新功能,如果新功能有bug还能直接回归源代码。
如何用装饰器
无参装饰器
推导
def index(x):
print('hello world %s'%x)
return 111
index(111)
# 加上功能,在其打印内容上下打印对应装饰内容
推导过程一
def index(x):
print('==========')
print('hello world %s'%x)
print('==========')
index(111)
# 直接在源代码内修改了
# 结论:直接修改了源代码
# 新问题:如何达到不修改源代码达成功能
推导过程二
def index(x):
print('hello world %s'%x)
return 111
def wrapper():
print('==========')
index(111)
print('==========')
wrapper()
# 函数嵌套,将index函数嵌套进另一个函数达成功能
# 结论:修改了调用方式,同时把函数index和函数wrapper写死了
# 新问题:如何把wrapper函数写活,指index处变成其他的函数名也可以运行
推导过程三
def index(x):
print('hello world %s'%x)
return 111
def wrapper(func):
print('==========')
func(111)
print('==========')
wrapper(index)
# 将原先wrapper内的index函数名变成func变量名将其写活,wrapper括号内写任何函数都可以调用
# 结论:修改了调用方式,函数index写死了
# 新问题:如何把index函数写活,指index()括号内可以传入值
推导过程四
def index(x):
print('hello world %s'%x)
return 111
def deco(func):
def wrapper(*args,**kwargs):
print('==========')
func(*args,**kwargs) # 可以对源代码index进行传参
print('==========')
return wrapper
index = deco(index) # index = 内存地址wrapper
# 将wrapper函数塞进deco函数内,将新index指向wrapper内存地址,可以对旧index函数进行传参
# 结论:源代码index返回值没了
# 新问题:如何完善装饰器wrapper函数,使其返回源代码的返回值
推导过程五
def deco(func):
def wrapper(*args,**kwargs):
print('==========')
res = func(*args,**kwargs) # 可以对源代码index进行传参
print('==========')
return res
return wrapper
@deco # index = deco(index的内存地址)
def index(x):
print('hello world %s'%x)
return 111
# wrapper返回index的返回值,通过@操作使代码更加简化
# 结论:先实现装饰器,在需要装饰的函数上加上@装饰器函数名即可
推导结果
对整个代码的变量名、函数名进行解释:
func-->被装饰的函数
wrapper-->装饰函数
deco-->对整个装饰函数进行封装
deco()-->wrapper内存地址源代码
index-->原本功能关键函数新变量名
index-->可以理解为经过装饰后的index源代码
无参装饰器结论(模板)
def outter(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
拓展
在此时,被装饰后的参数已经伪装的与源代码差不多了,但是在help参数或print参数内存地址时还是会暴露出,这个参数被进行装饰过。如何使其伪装的更像源代码?使用wraps函数,以下是操作过程
from functolls import wrapsdef deco(func):
@wraps(func)
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper# wraps
函数把一堆源代码参数赋值给wrapper参数
多个装饰器具体流程
@deco1 # 假设在被装饰代码上输入111
@deco2 # 假设在被装饰代码上输入222
@deco3 # 假设在被装饰代码上输入333
def index():
print('hello world!')
index()
# 输出结果
111
222
333
hello world!
具体过程:deco1输入111,执行deco2,deco2输入222,执行deco3,deco3输入333,执行index源代码,执行完后返回deco3,deco3后没有装饰代码,返回deco2,deco2后没有装饰代码,返回deco1,deco1后没有装饰代码,执行完毕。 此时deco3装饰的是index源代码,deco2装饰的是deco3,deco1装饰的是deco2。
有参装饰器
在装饰器内需要外部传入一个参数时如何进行操作
在无参装饰器中
def outter(func,mode=0):
def wrapper(*args,**kwargs):
if mode == 0: # 假设这里需要一个变量mode进行判断是否进行以下操作
res = func(*args,**kwargs)
return res
return wrapper
首先,wrapper函数是不可以添加形参的,因为这个函数名的形参全部传给源代码
可以尝试将导入的参数放进outter函数,成为outter函数的形参,但是如果这么使用的话,就无法使用语法糖,即@操作
其实我们完全可以进行如下操作
def outtter(mode=0):
def outter(func):...
return outter
outter = outtter(mode=1)
将outter函数缩略后其实它就是一个函数,在其外面包一层函数给它需要的参数传值即可
再简化一下
def outtter(mode=0):
def outter(func):...
return outter
@outtter(mode=1)
需要被装饰的函数
其实@这一步操作就是返回了outter内存地址,同时把它需要的参数mode给传递了,以后改变mode值只需要在@后的mode进行修改即可
结论
如果装饰器内部需要传参,在其外部套一层函数,将需要传的参数变成其形参,在外层函数末尾return下一级的函数的内存地址在需要使用语法糖操作时,直接在需要装饰的源代码上@外部参数(你需要的参数)即可
模板
def outtter(mode=0):
def outter(func):...
return outter
@outtter(mode=1)
需要被装饰的函数