一.什么是装饰器
装饰器是用来给函数动态的添加功能的一种技术,属于一种语法糖。通俗一点讲就是:在不会影响原有函数的功能基础上,在原有函数的执行过程中额外的添加上另外一段处理逻辑
二.装饰器功能实现的技术基础--闭包
什么是闭包?闭包就是:一个内部函数被一个外部函数当做返回值进行返回,并且内部函数引用了外部函数提供的变量, 此时将内部函数和引用的外部变量构成的整体称为闭包
闭包的特征?闭包有一个明显的特征就是:引用了外部变量的闭包能够让外部函数不被释放,如果外部函数被释放,就会导致内部函数访问变量时出错
闭包中内部函数如何修改外部函数提供的变量? python3 如果要在闭包内修改外部函数提供的变量,需要使用(nonlocal 变量名称) 进行声明变量不是本地变量,才能进行更改 , python2 中,需要在闭包外先将变量添加到一个列表里,再在闭包内通过下标取出变量,然后进行更改使用。
三.装饰器的使用场景
引入日志
函数执行时间统计
执行函数前预备处理
执行函数后清理功能
权限校验等场景
缓存
三.不带参的装饰器的使用与流程分析
装饰阶段:调用外层函数 (在调用被装饰函数前,已经经历装饰阶段)
运行阶段:调用内层函数和 内层函数的函数体中的func指向的 装饰器下的函数 (运行阶段就是调用被装饰函数的时候)
#coding=utf-8
from django.shortcuts importredirectfrom django.http importHttpResponseRedirectfrom rest_framework.response importResponsedef login(func): #登录验证装饰器,如果未登录就转到登录页面
def login_func(request, *args, **kwargs)if user_id inrequest.sessionreturn func(request, *args, **kwargs)else:
返回还是一个response对象,可以用来设置cookie,session等
redi= HttpResponseRedirect('./user/login')#设置cookie,当用户尚未登录时就进行需要登录后才能进行的操作,
#就先记住用户的当前所处的页面,登录时,通过取回cookie则将用户登录前所处的页面返回
redi.set_cookies('url', request.get_full_path())returnredireturnlogin_func # 返回时使用了变量名称 api_list 进行接收
@logindefapi_list(request):return Response(status=status.HTTP_200_OK)
api_list(request) # 假设存在这么一个调用逻辑。当然了,在实际的接口,并不是我们开发人员手动去调用的,有用户请求了才会触发。#request.get_full_path() 获取带参数的当前请求所在的页面的url#request.path 获取去掉参数的当前请求所在的页面的url
流程说明:
1.装饰阶段
首先,会存在这么一个执行流程(这是解释器去进行的):
api_list = login(api_list),
将被装饰的函数的引用进行传参,调用装饰器的外层函数,返回内层函数的引用, 返回值使用了被装饰函数的函数名称进行接收,此时的状态就是:
api_list 指向了 原来 login_func 包含的函数体,func 指向了 原来api_list所指向的函数体,注意是 原来!
2.运行阶段
当使用 ret = api_list(request) 进行函数的调用时,因为 api_list 已经指向了 原来 login_func 所包含的函数体,也就是装饰器的内层
函数。所以,装饰器的内层函数开始执行,func被调用,因为此时 func 指向了原来 api_list 所包含的函数体,最后,被装饰函数得到执行
注意:内层函数也同样要接收 被装饰函数所接收了的参数
四.带参的装饰器的使用与流程分析
#装饰阶段(装饰阶段时解释器自动进行这段逻辑的):#1.beter = outer("lowman"), 结果是创建出新的装饰器 @beter,然后继续进行装饰,就是下面第二步#2.zhuangshi = beter(zhuangshi),结果为 zhuangshi 引用指向 原来 inner 的函数体, func 指向了 原来 zhuangshi 的函数体#运行阶段:#1.调用 zhuangshi(last_name) 时,实际调用的是 inner(last_name, *args, ** kwargs)#2.调用 inner 内层函数体中的 func(last_name, * args, **kwargs) 时,实际调用的是 原来 def zhuangshi(last_name) 的函数体# 带参与不带参的使用区别:
# 带参装饰器与不带参的装饰器的区别仅仅就是带参装饰器可以实现通过装饰器向闭包中传递参数,仅此而已。
defouter(item):defbeter(func):
first_name=itemdef inner(last_name, *args, **kwargs):
ret= func(last_name, * args, **kwargs)return first_name +retreturninnerreturnbeter
@outer("lowman")defzhuangshi(last_name):returnlast_name
ret= zhuangshi("isbusy") #调用zhuangshi函数,实际调用的是 内层函数inner, 随后 func 函数被执行, 被装饰函数最终也得以执行
print(ret)#打印结果: lowmanisbusy
注意:
多个装饰器装饰一个函数:#1.装饰阶段:先装饰 内层装饰器,再装饰 外层装饰器;
#2.运行阶段:先执行 外层装饰器,再执行 内层装饰器;