什么是装饰器?
装饰器本质是一个函数,它的功能是为其他函数添加附加功能。
设计装饰器的原则:
- 不修改被修改函数的源代码
- 不修改被修改函数的调用方式
装饰器应用场景:
项目上线之后,不能修改源代码,一旦出错就得背锅。所以需要用装饰器这种东西来给写好的函数加新的功能。
具体实现装饰器应该怎么做?
装饰器=高阶函数+函数嵌套+闭包
装饰器本质上就是一个函数,千万不要忘记。
先写一个函数(要上线的代码)。
def pr():
print('vthnb!')
然后发现只有英文的,不爽,想要加上中文,咋办呢?
def wrapper_out(func):
def wrapper():
func()
print('vth牛逼')
return wrapper
@wrapper_out # 等价于pr = wrapper_out(pr)
def pr():
print('vthnb!')
pr()
这样就装饰好了。怎么装饰的?无非就是把函数调用与另外一些要加上的代码写在一起放在一个函数中,为了不修改原本函数的调用方式,所以要把这个函数重新赋值给原函数名。
白话版:你嫌弃电脑散热不好,修电脑小哥给你装了个外置散热器,你的电脑该咋开机还是该咋开机,该叫啥还是该叫啥。
用着用着会发现还是不爽,要是原来的函数带返回值咋办?
上面那段话如果你看懂了是怎么制作装饰器的,想明白怎么带返回值应该不难。无非就是设置一个变量接收一下函数掉用中的返回值,然后再把这个返回值返回即可。
def wrapper_out(func):
def wrapper():
res = func()
print('vth牛逼')
return res
return wrapper
@wrapper_out # 等价于pr = wrapper_out(pr)
def pr():
print('vthnb!')
return 666
res = pr()
print(res)
然后你突然想到要是带参数咋办?好问题。
你给装饰器丢一个func
,装饰器丢回来的是wrapper
所以你函数名字虽然没变,但是具体执行的函数的确变成了wrapper
。那给func
传参,意思就是给wrapper
传参,然后这个参数还得交给wrapper
内层的func
。
def wrapper_out(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
print('vth牛逼')
return res
return wrapper
@wrapper_out # 等价于pr = wrapper_out(pr)
def pr(*args,**kwargs):
print(args)
return 666
res = pr('faker')
print(res)
接连搞定了普通装饰器,返回值,参数。你又在思考,咦,装饰器不是一个函数吗?为啥不能给装饰器传参呢? 这个当然是可以的,只不过要在装饰器外面再套一层函数。(为什么不直接传给装饰器呢?因为装饰器的参数是func
呀)
def wrapper_out_out(name='vth'):
def wrapper_out(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
if name == 'vth':
print('vth牛逼')
else:
print('你丫谁啊')
return res
return wrapper
return wrapper_out
@wrapper_out_out('faker') # 等价于pr = wrapper_out(pr)
def pr(*args,**kwargs):
print(args)
return 666
res = pr('faker')
print(res)
一定要注意,想给装饰器传额外参数时,用@
语法糖操作时,一定要以函数调用的方式来弄。为啥呢?因为@
后面跟的是装饰器,谁是装饰器?wrapper_out
才是装饰器,怎么拿到wrapper_out
呢?只能是执行wrapper_out_out
才能返回wrapper_out
。所以这里要写成函数调用的方式来拿到装饰器。
到此,装饰器就说完啦。补充一个知识点。
a,*_,c = [1,2,3,4,5,6,7,8]
print(a,_,c)
输出:
1 [2, 3, 4, 5, 6, 7] 8
*代表接收无限量的元素,跟参数*args差不多
而且,别管接收的是什么序列,_中存储的都是列表