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

2022-09-16 10:49:54

装饰器


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

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

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

例子:

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

尝试1

defouter(origin):definner():print('before')
        result= origin()print('after')return resultreturn innerdeffunc():print("我是func函数")
    value=(11,22,33,44)return value

func= outer(func)

result= func()print(result)

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

尝试2

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

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

@函数名defxxx():pass

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

代码见下:

defouter(origin):definner():print('before')
        result= origin()# 调用原来的func函数print('after')return resultreturn inner@outer# func = outer(func)  相当于func是innerdeffunc():print("我是func函数")
    value=(11,22,33,44)return value# result = outer(func)()
result= func()print(result)

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

尝试3

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

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

defouter(origin):definner():print('before')
        result= origin()print('after')return resultreturn inner@outer# func1 = outer(func1)    本质就是innerdeffunc1():print("我是func1函数")
    value=(11,22,33,44)return value@outer# func2 = outer(func2)deffunc2():print("我是func2函数")
    value=(11,22,33,44)return value@outer# func3 = outer(func3)deffunc3():print("我是func3函数")
    value=(11,22,33,44)return value

func1()
func2()
func3()

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

优化

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

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

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

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

输出的结果是:

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

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

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

###################### 函数有参数defouter(origin):definner(*args,**kwargs):# 参数、关键字参数print('before')
        result= origin(*args,**kwargs)# 调用原来的func函数,调用也可以传*args,**kwargsprint('after')return resultreturn inner@outer# func1 = outer(func1)    本质就是innerdeffunc1(a1):# 一个参数print("我是func1函数")
    value=(11,22,33,44)return value@outer# func2 = outer(func2)deffunc2(a1, a2):# 两个参数print("我是func2函数")
    value=(11,22,33,44)return value@outer# func3 = outer(func3)deffunc3():# 无参数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 functoolsdefauth(func):@functools.wraps(func)# inner.__name__ = func.__name__; inner.__doc = func.__doc__definner(*args,**kwargs):'''巴拉巴拉'''# 代码前
        res= func(*args,**kwargs)# 执行原函数return resreturn inner@authdefadmin():'''这是一个xxx的函数'''print('123')defrbac():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 functoolsdefouter(origin):@functools.wraps(origin)# inner.__name__ = func.__name__; inner.__doc = func.__doc__definner(*args,**kwargs):# 执行前自定义功能
            res= origin(*args,**kwargs)# 调用原来的func函数# 执行后自定义功能return resreturn inner@outer# 相当于 func = outer(func)  那么func == innerdeffunc():pass
    
    func()
  • 作者:Grit_Lrj
  • 原文链接:https://blog.csdn.net/weixin_43915382/article/details/120961504
    更新时间:2022-09-16 10:49:54