Loading

装饰器

装饰器

在python中,装饰器的本质就是一个函数,它可以让其他函数在不需要做任何变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽象出大量与函数功能本身无关的雷同代码并继续重用。
概括地讲,装饰器的作用就是为已经存在的对象添加额外的功能。

1 装饰无参数函数

def outer(func):  # 定义装饰器函数,有一个参数func,用于导入被装饰的函数
    def inner():  # 定义装饰好的,添加了新功能的函数
        print("这里写函数执行前要添加的功能")
        r = func()  # 运行被装饰函数,并用r接收被装饰函数运行后的返回值
        print("这里写函数执行后要添加的功能")
        return r  # 将被装饰函数的返回值(即r)写入新函数的返回值
    return inner  # 将新函数的函数名当做装饰器函数的返回值
"""
接下来使用装饰器,用关键字@+装饰器函数名,@语法有两个作用:
1.执行装饰器函数outer,并将下一句的函数名(即foo)当做参数;
2.将装饰器函数outer的返回值重新赋值给下一句的函数
"""
@outer
def foo():
    print("I'm a foo!")
foo()  # 此时的foo函数内部已经被装饰成了新函数,执行即可.


输出:
>>>这里写函数执行前要添加的功能
>>>I'm a foo!
>>>这里写函数执行后要添加的功能

可以看到,在没有修改原函数foo的前提下,我们在运行foo()时为其添加了我们想要的功能.

例1:

def outer(func):
    return "123"
@outer
def f1():
    return True
print(f1)


输出:
>>>123

解释器执行到@outer后,首先将f1当做参数传入outer函数内,然后将outer的返回值重新赋值给f1,即f1指向返回值的内存地址,因此这时的f1不是函数而是一个指向字符串的变量名.

image

思考一个问题,既然f1可以指向字符串,那么是不是也可以让它指向一个函数呢?换句话说,把例1中的”123”改为另一个函数,那么f1运行的就是另一个函数.见例2:

def outer(func):
    def f2():
        pass
    return f2
@outer
def f1():
return True

这样一来,f1就指向了f2函数,运行f1()时,就相当于运行了f2().

如此一来有一个问题,f1已经被f2取代了,这不符合我们给原函数添加功能的目的,还需要做进一步修改.

例3:

def outer(func):
    def f2():
        r = func()
        return r
    return f2
@outer
def f1():
return True

我们在f2函数内部执行f1,并将f1的返回值当做自己的返回值返回,这样一来,运行f1就等于运行f2,而f2内部包含原f1函数.一个装饰器的模型就出现了.

2 装饰有参数函数

def outer(func):
    def inner(v1,v2):
        print("新增功能")
        r = func(v1,v2)
        return r
    return inner
@outer
def f1(x,y):
    print("这是一个带参数的函数")
    return x+y
print(f1(1,2))


输出:
>>>新增功能
>>>这是一个带参数的函数
>>>3

如果原函数有参数,那么定义inner函数时加上对应参数即可.

3 装饰带有N个参数的函数

def outer(func):
    def inner(*args,**kwargs):
        print("新增功能")
        r = func(*args,**kwargs)
        return r
    return inner
@outer
def f1(x,y,*args,**kwargs):
    print("这是一个带有N个参数的函数")
    return [x,y,args,kwargs]
r = f1(1,2,"aaa",k="bbb")
print(r)


输出:
>>>新增功能
>>>这是一个带有N个参数的函数
>>>[1, 2, ('aaa',), {'k': 'bbb'}]

依葫芦画瓢,不再赘述,此例中的装饰器可以装饰含有N个参数的函数.

4 多个装饰器装饰一个函数

def outer0(func):
    print("outer0")
    def inner0(*args, **kwargs):
        print("inner01")
        r = func(*args, **kwargs)
        print("inner02")
        return r
    return inner0

def outer1(func):
    print("outer1")
    def inner1(*args, **kwargs):
        print("inner11")
        r = func(*args, **kwargs)
        print("inner12")
        return r
    return inner1

def outer2(func):
    print("outer2")
    def inner2(*args, **kwargs):
        print("inner21")
        r = func(*args, **kwargs)
        print("inner22")
        return r
    return inner2

@outer0
@outer1
@outer2
def f1(x, y):
    print("111")
    return x + y


ret = f1(1, 2)
print(ret)


输出:
>>>outer2
>>>outer1
>>>outer0
>>>inner01
>>>inner11
>>>inner21
>>>111
>>>inner22
>>>inner12
>>>inner02
>>>3

简述一下执行过程:

多个装饰器装饰同一个函数时,优先使用最近的装饰器。即:@outer2-->@outer1-->@outer0

可以理解为“从下到上”进行封装。

image

posted @ 2021-10-12 03:37  yyyz  阅读(67)  评论(0编辑  收藏  举报