装饰器
装饰器来自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分