Decorators学习
Kale

装饰器顾名思义,其实就是起到装饰作用的一个函数,也可以理解为为使用装饰器的函数增加功能.

要了解装饰器,首先需要了解闭包,简单写一个闭包:

1
2
3
4
5
6
7
8
9
10
def outer(a):
def wrapper(b):
print(b)
return a
return wrapper


if __name__ == '__main__':
demo = outer('outer')
print(demo('inner'))

函数结果为inner,outer
分析结果,先定义变量demo指向outer的地址,给outer函数传了一个参数为outer,outer函数运行时,内层定义了一个wrapper函数,然后返回这个函数,则变量demo此时指向的是wrapper函数,接着print demo对象,并传入一个参数inner,此时开始执行内层函数wrapper,先输出内层函数接收到的参数b,即inner,然后返回外层函数的参数a,即outer.所以最后的结果是先输出inner,后输出outer.这就构成了一个闭包.

一般函数在运行结束后会释放内存,但是闭包是一种特殊情况,外层函数在运行结束时发现自己的临时变量会在内层函数中用到的话,就会将临时变量与内层函数绑定,不释放出内存,所以内层函数仍然可以调用完成函数的临时变量.

装饰器其实就是闭包的一种应用,装饰器是给函数加功能,那么被装饰函数就可以理解为内层函数,通过构造闭包,就可以给内层函数加功能了.

之前在项目中用户第一次登录后端会发送一个有时效的token给前端,前端放在请求头里,如果需要进行验证的话,可以写一个验证函数,然后在每个controller里都调用函数.但是已经实现好的功能最好是不要变动的,所以可以用到装饰器,直接在外层给函数添加功能.

1
2
3
4
5
6
7
8
9
10
11
def check_token(func):
def wrapper(*args, **kwargs):
token = args[0].headers['Authorization']
salt = 'qwer'
t = itsdangerous.TimedJSONWebSignatureSerializer(salt, expires_in=3600)
try:
res = t.loads(token)
except:
return JsonResponse({'msg': '您的身份已经过期, 请重新登录'}, safe=False)
return func(*args, **kwargs)
return wrapper

可以看到里面的wrapper其实就是被装饰的函数,给里面的函数加了验证token的功能,这里直接return的话没进行完的被装饰的函数也会直接返回.

这里的参数有*args**kwargs,一开始这一块还踩坑了,*args其实是不管输入的参数是什么,都打包成一个元组.(<WSGIRequest: GET '/getMyInfo'>,)不注意可能以为就是一个字符串或者对象,其实是一个元组,元组里面只有一个对象,当然因为被装饰函数只有一个参数,所以这里元组只有一个对象.而**kdwargs是可以将参数转化为字典.

1
2
3
4
5
6
7
8
9
10
def outer(a):
def wrapper(**kwargs):
print(kwargs)
return a
return wrapper


if __name__ == '__main__':
demo = outer('outer')
print(demo(a=3, b=4))

还是刚刚的例子,将传入参数变成a=3, b=4,输出为{'a': 3, 'b': 4},可以看到参数被打包成了字典.

另外,如果装饰器需要传参,那么可以很容易想到,再给装饰器装饰一层就可以了,套娃就可以了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def outer(text):
def decorator(func):
def wrapper(*args, **kwargs):
print(args)
print(text)
return func(*args, **kwargs)
return wrapper
return decorator


@outer('hello')
def add(a, b):
return a + b


if __name__ == '__main__':
add(2, 4)

输出为(2, 4) hello,输出了hello,则表示最内层函数成功取到了装饰器传入的参数.

关于装饰器其实还有@property``@staticmethod``@classmethod三类,回头有时间再看了.


补充一下类装饰器,前面的函数装饰器也可以写成类的形式,通过__call__调用就可以.
__call__是在定义类型的时候,只要实现了__call__函数,这个类型就成为可以调用的,可以当做函数来用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class check_token:
def __init__(self, func):
self.func = func

def __call__(self, *args, **kwargs):
print(args)
return self.func(*args, **kwargs)


@check_token
def hello(text):
print(text)


if __name__ == '__main__':
hello('hello')

类装饰器也可以接收参数,只要在__call__里套娃就可以了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class check_token:
def __init__(self, level='INFO'):
self.level = level

def __call__(self, func):
def wrapper(*args, **kwargs):
print(self.level)
return func(*args, **kwargs)
return wrapper


@check_token('world')
def hello(text):
print(text)


if __name__ == '__main__':
hello('hello')

输出结果为world hello

  • 本文标题:Decorators学习
  • 本文作者:Kale
  • 创建时间:2020-04-09 19:04:37
  • 本文链接:https://kalew515.com/2020/04/09/Decorators学习/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!