python学习day24笔记

今日内容

装饰器

什么是装饰器

装饰器是一个用来为被装饰者添加功能的工具

被装饰者:函数
装饰器:可以用函数实现装饰器这种工具

为何要用装饰器

不影响源代码,调用方式不改变,同时增加新功能,如果新功能有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)
需要被装饰的函数
posted @ 2021-07-05 20:40  麋鹿的麋  阅读(39)  评论(0)    收藏  举报