装饰器

1、装饰器本质上是一个函数,其左右是在不改变原有代码的基础上扩展功能。装饰器符合开闭原则,即原函数不能变,调用不能变,对扩展开发,对修改封闭

2、装饰器的原理

def f1(x):   # 定义函数,计算参数的平方值
    return x*x
def func_new(func):   # 定义一个装饰器,作用是在f1函数输出结果的前一行加上“正在计算”
    def fn(j):
        print('正在计算')
        return func(j)
    return fn
y = func_new(f1) 
print(y(10))

整个代码的运行逻辑是:

  • 定义了函数 f1 和 func_new,并用函数名 y 接收了 func_new 函数的返回值
  • 执行 func_new,将参数 f1 传入,在执行途中定义了函数 fn,并返回函数 fn
  • 走到下一行,在 func_new 函数中,发现 fn 已被定义,并在最后一行中给 fn 赋值10(即j=10),开始执行 fn
  • 输出“正在计算”,之后返回 func(10)。由于之前已经将 f1 赋值给func函数,故将计算10的平方,后将上面两条输出返回
  • 返回的值被 y 接收,最终打印出我们所需的两行结果
正在计算
100

在上面的代码中,我们可以将接收返回值的 y 改成 f1,这么一来,在未来调用函数 f1 时,将会同时出现上面两条结果,达到了装饰器的目的

3、装饰器的写法

def decorator(func):  #定义一个装饰器,作用是在所装饰函数的输出前一行加上10个横杠
    def inner():
        print('-'*10)
        func()
    return inner

@decorator   #在这里用 @装饰器名 即可调用装饰器
def show():
    print('abc')
show()

在上面的代码中,用 @decorator 即可代表 show=decorator(show),show 指向 inner

输出的结果如下。往后当每次调用 show() 时,都会输出下面的结果

----------
abc

4、装饰带有参数的函数

def deco(func):
    def inner(x):
        print('-'*10)
        func(x)
    return inner

@deco
def show(x):
    print(x)
show('abc')

与前一个例子的区别在于,本例在设置装饰器时需要传入参数x(此处的x其实可以用其他参数名代替),与所装饰函数的参数进行对应

5、通用的装饰器,即装饰器在使用时不受所装饰函数是否有参数、参数是什么类型的影响

def deco(func):   #在所装饰的函数输出前一行加上十个横杠
    def inner(*args,**kwargs):   
    #使用不定长位置参数、不定长关键字参数,使得装饰器能够接收所装饰函数的任何参数
        print('-'*10)
        return func(*args,**kwargs)
    return inner

@deco
def show(num1,num2):
    result = num1 + num2
    print(result)
    return result
show(1,2)

6、带有参数的装饰器

def deco2(char):   #根据参数char的值,在所修饰函数输出结果的前一行加上十个char
    def deco1(func):
        def inner(*args,**kwargs):
            print(char*10)
            return func(*args,**kwargs)
        return inner
    return deco1

@deco2('-')   #此处记得给装饰器传参
def show():
    print('abc')
show()

7、函数使用多个装饰器

def deco1(func):   #在所装饰的函数输出前一行加上十个横杠
    def inner1():
        print('-'*10)
        return func()
    return inner1

def deco2(func):   #在所装饰的函数输出前一行加上十个星号
    def inner2():
        print('*'*10)
        return func()
    return inner2

@deco1
@deco2
def show():
    print('abc')
show()

show函数使用了两个装饰器,输出的结果如下。

----------
**********
abc

在这里我们需要注意装饰的调用顺序。在这个例子中,是先装饰deco2,再装饰deco1。整个代码的运行逻辑如下:

  • 代码中定义了两个装饰器和一个函数,并在最后调用函数
  • 在装饰顺序中,先调用离函数最近的装饰器,故开始调用deco2,即show=deco2(show),此时show指向deco2的内部函数inner2,执行完毕后返回inner2
  • 之后执行deco1。此时show已经被inner2赋值,故将inner2传入deco1,即inner2=deco1(inner2),inner2(或者说,show)指向inner1,执行完毕后返回inner1
  • 此时show指向inner1,在执行show的时候,从deco1开始执行。首先打印10个横杠,由于inner1内的func已经被inner2赋值,故往下走后直接转至inner2执行
  • 在inner2内,先打印10个星号,由于inner2内的func被show赋值,故执行show(),打印“abc”
  • 至此整个流程结束

可以通俗理解为,函数是人的身体,离函数最近的装饰器是内衣,之后的装饰器是外套。穿衣服(调用装饰器)时,是先穿内衣后穿外套;脱衣服(执行装饰器)时,是先脱外套再脱内衣

8、解除装饰器的作用
之前曾提到,用装饰器装饰函数后,往后当调用函数时,输出的结果将会加上装饰器的效果;同时如想看到原函数的文档说明,在使用装饰器后,通过原函数名只能查看到装饰器的文档说明。
这些都表明,在使用装饰器后,对原函数的引用做了修改。如果想解除装饰器对该函数的作用,可以导入functools模块中的wraps方法进行解决

from functools import wraps

def decorator(func):
    @wraps(func)
    def inner():
        '''inner'''
        print('-'*10)
        func()
    return inner

@decorator
def show():
    '''show'''
    print('abc')

show.__wrapped__()   #输出不带装饰器效果的原函数的值
print(show.__doc__)   #输出原函数的文本说明
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值