一文带你彻底了解python装饰器decorator

装饰器


装饰器来自Decorator的直译,理解装饰这个词就等于理解了装饰器。

在 python 中的装饰器则是提供了一些额外的功能。

下面通过一个例子进行装饰器的介绍。

例子:

‘’’
需求:实现func函数执行前输出一个before,执行后输出after
‘’’

尝试1

def outer(origin):
    def inner():    
        print('before')
        result = origin()
        print('after')
        return result
    return inner

def func():
    print("我是func函数")
    value = (11, 22, 33, 44)
    return value

func = outer(func)

result = func()
print(result)

这个比func函数内直接添加print 差

尝试2

开始使用装饰器,python中支持特殊语法:@函数名

python中支持特殊语法,在某个函数上方使用:@函数名

@函数名  
def xxx():  
    pass

python内部会自动执行 函数名(xxx), 执行完之后再将结果赋值给xxx
相当于:
xxx = 函数名(xxx)

代码见下:

def outer(origin):
    def inner():    
        print('before')
        result = origin()    # 调用原来的func函数
        print('after')
        return result
    return inner

@outer    # func = outer(func)  相当于func是inner
def func():
    print("我是func函数")
    value = (11, 22, 33, 44)
    return value

# result = outer(func)()
result = func()
print(result)

虽然看起来代码好一些了,但是还是繁琐

尝试3

更改需求,要求在三个函数执行前都输出before,执行后都输出after

根据第二回合可知:直接在每一个func函数前添加 @函数名 即可统一修改

def outer(origin):
    def inner():
        print('before')
        result = origin()
        print('after')
        return result
    return inner

@outer    # func1 = outer(func1)    本质就是inner
def func1():
    print("我是func1函数")
    value = (11, 22, 33, 44)
    return value

@outer    # func2 = outer(func2)
def func2():
    print("我是func2函数")
    value = (11, 22, 33, 44)
    return value

@outer    # func3 = outer(func3)
def func3():
    print("我是func3函数")
    value = (11, 22, 33, 44)
    return value

func1()
func2()
func3()

由此可知:装饰器应用场景:函数少的话直接函数内部改,多的话采用装饰器

优化

之前运用装饰器的例子我们可以发现函数都是没有参数的,然而实际情况中的函数可能是有多个参数,并且函数系统每个函数可能参数个数都不同,有的甚至会有关键字参数。
这时我们就要用到*args,**kwargs。

这里先讲一下什么是*args和**kwargs?

话不多说,直接上例子一目了然

def test(a,*args,**kwargs):
    print a
    # print b
    # print c
    print args
    print kwargs
 
test(1,2,3,d='4',e=5)

输出的结果是:

1
(2, 3)
{‘e’: 5, ‘d’: ‘4’}

意思就是1还是参数a的值,args表示剩余的值,kwargs在args之后表示键值对。

明白了如果传入多个参数和关键字参数,下面就给出有n个参数的装饰器的代码:

###################### 函数有参数

def outer(origin):
    def inner(*args, **kwargs):    # 参数、关键字参数
        print('before')
        result = origin(*args, **kwargs)    # 调用原来的func函数,调用也可以传*args,**kwargs
        print('after')
        return result
    return inner

@outer    # func1 = outer(func1)    本质就是inner
def func1(a1):    # 一个参数
    print("我是func1函数")
    value = (11, 22, 33, 44)
    return value

@outer    # func2 = outer(func2)
def func2(a1, a2):    # 两个参数
    print("我是func2函数")
    value = (11, 22, 33, 44)
    return value

@outer    # func3 = outer(func3)
def func3():    # 无参数
    print("我是func3函数")
    value = (11, 22, 33, 44)
    return value

func1(1)
func2(11, a2 = 22)
func3()

在第29-31行我们调用了这3个函数,我们知道在运用**@outer时,相当于给当前函数func赋值outer(func),由于函数outer返回的是内嵌函数inner,所以inner应当与func函数有相同的参数,因此代码第4行inner函数需要有参数**。另外,由于闭包中的origin就是原函数func,所以origin也应该传参。这样就实现了装饰器支持n个参数。

进一步优化

在装饰器使用过程中,可能会遇到调用.__name__和.__doc__需要返回原先函数的内容。这里就需要用到functools库。如果不使用functools,返回的是inner函数的内容。

直接上代码:

import functools

def auth(func):
    @functools.wraps(func)    # inner.__name__ = func.__name__; inner.__doc = func.__doc__
    def inner(*args, **kwargs):
        '''巴拉巴拉'''
        
        # 代码前
        res = func(*args, **kwargs)    # 执行原函数
        return res

    return inner

@auth
def admin():
    '''这是一个xxx的函数'''
    print('123')

def rbac():
    print("rbac")

##### 没有添加装饰器前
# 函数名 + ()  调用函数
# admin()
# print(admin.__name__)    # "admin" 函数名字符串
# print(admin.__doc__)    # 输出admin函数的注释


##### 添加装饰器后 
print(admin.__name__)    # "inner"
print(admin.__doc__)    # 巴拉巴拉
# 说明装饰器之后 这个admin就是inner了,而inner里面的func代指原来的admin函数

'''
如果想使用装饰器之后 仍然想输出原来函数的名字和注释 需要用到functools
在inner函数前添加@functools.wraps(func)即可实现 
这个玩意使得装饰器不仅能够执行inner函数扩展功能,而且可以让inner伪装的更像在执行原来的函数似的
'''
print(admin.__name__)    # "admin" 函数名字符串
print(admin.__doc__)    # 这是一个xxx的函数

总结

  • 实现原理:基于@语法和函数闭包,将原函数(func)封装在闭包中,然后将函数赋值为一个新的函数(内层函数inner),执行函数时再在内层函数中执行闭包中的原函数。(因为@函数名 == 函数名(xxx) == 内层函数inner,相当于调用func就是直接运行inner。)
  • 实现效果:可以在不改变原函数内部代码和调用方式的前提下,实现在函数执行和执行扩展功能。
  • 使用场景:多个函数系统统一在执行前后自定义一些功能。
  • 调用functools使得使用装饰器后的函数伪装的更像原函数
  • 装饰器示例:(一定要熟记)
    import functools
    
    def outer(origin):
        @functools.wraps(origin)    # inner.__name__ = func.__name__; inner.__doc = func.__doc__
        def inner(*args, **kwargs):
            # 执行前自定义功能
            res = origin(*args, **kwargs)    # 调用原来的func函数
            # 执行后自定义功能
            return res
        return inner
    
    
    @outer    # 相当于 func = outer(func)  那么func == inner
    def func():
        pass
    
    func()
    

参考资料:b站:彻底学会装饰器。感谢up主!

2021年10月25日21点42分

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值