装饰器

三、装饰器

内裤可以用来遮羞,但到了冬天它没办法帮我们防风御寒,聪明的人们发明了长裤,穿上了长裤人们再也不冷了。装饰器就像这里说的长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效。
在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不是函数而是一个指向字符串的变量名.

装饰器

思考一个问题,既然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):
    def inner(*args,**kwargs):
        print("新增功能1")
        r = func(*args,**kwargs)
        return r
    return inner
def outer1(func):
    def inner(*args,**kwargs):
        print("新增功能2")
        r = func(*args,**kwargs)
        return r
    return inner
@outer0
@outer1
def f1(x,y):
    print("111")
    return x+y
ret = f1(1,2)
print(ret)


输出:
>>>新增功能1
>>>新增功能2
>>>111
>>>3

这里@outer1和@outer2,新增了两个功能.

可见,一个函数可以应用多个装饰器,按顺序@即可

上一篇:IO流 File类下中的判断和获取方法


下一篇:PAT A1009