闭包 --装饰器的前提
- 闭包的介绍
- 闭包的概念
- 函数式编程的重要的语法结构。
- 闭包(Closure)是由函数和与其相关的引用环境(在闭包中使用的装饰器中的变量)组合而成的实体(对象)。
- 这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
- 闭包的特点
- 定义在函数内部并使用外部函数提供的环境变量
- 闭包不是简单的函数,可以根据不同环境产生不同函数对象实例(每一次引用闭包都会创建一个不同闭包实例)
- 作用
- 可以不更改函数的逻辑代码,通过闭包来实现对函数功能的拓展。
- 闭包实例的使用方法
- 闭包的使用方式
- 建立一个含有闭包的函数(装饰器)
- 调用装饰器函数,返回一个闭包的实例(实例中包含了环境变量和函数)
- 调用闭包实例执行
- 闭包存在的属性
- __closure__ 属性
- “其中包含了所有环境变量的引用于同一个元组中, 可以通过对该实例属性的修改来完成修改环境变量”
- 对于环境变量有cell_contents 方法, 用来表明变量对象中包含的内容类型和名称等
- 注:元组中的第一位为创建出的闭包实例的引用,后面为对象的环境变量的引用
- __name__ 属性
- “表示该闭包实例的名称,默认为闭包函数名, 可以通过修改该属性来表示闭包所关联的函数,方便区分闭包”
- 闭包修改环境变量
- py2 和py3修改环境变量的方式
- python3 修改环境变量的方法
- 使用nonlocal来声明下面出现的变量都不是环境变量,是在外边的函数中的变量;
- 修改变量的时候不会在局部空间创建地址,会寻找对应的环境变量(不包括全局变量)来进行修改
- python2 修改环境变量的方法
- py2则需要 将环境数据包装在列表中
- 然后通过对列表的增删改来达到修改外部变量的目的
- 闭包装饰器
- 包含了闭包的函数极称为装饰器函数,即为下面实例的gettime函数,返回的是被装饰的 func的函数对象即为inner
- 闭包的实例
更进一步的@语法糖--装饰器
- 例子:
- 备注:
- func函数可能有参数
- 给inner增加需要参数 *args,**kwargs
- func可能有返回值
- 暂存func的返回值 ret(如果需func的返回值的话,不单单是执行func)
- 在inner中最后return ret
- 作用:
- @gettime ==》 func = gettime(func) “如果用了@语法,py解释器会自动的执行其对应的含义,不需要代码去对其进行启动”
- 定义函数的时候直接用@装饰器函数,可以对代码进行修饰
- 用户使用装饰后的代码时,会与代码的修改无感知(既然从使用上不会造成什么改变,但是执行结果上已经被修饰过)
装饰器工厂
- 概念
- 从前面的了解可以知道,创建闭包的外部函数就叫装饰器
- 而装饰器工厂可以理解为生产装饰器的函数
- 作用:
- 现有装饰器只能接受被装饰函数作为参数,不能接收其他参数
- 创建一个装饰器工厂函数根据参数不同返回不同的装饰后的函数(就是装饰器只能传入函数为参数,所以想要传入其他参数需要用装饰器工厂来传入)。
- 特征:
- 返回值为装饰器对象
- 可以根据不同的参数对执行代码进行不同的配置
- 例子:
- @语法在此处的作用
- get_run_time(True) ==> func = get_run_time(True)(func)
- 装饰器工厂的一种用法
- 给web服务器添加路由
- 实现了通过给装饰器工程传入不同的文件和函数的匹配信息,完成不同的装饰效果
装饰器的副作用
- 影响:
- 被装饰之后的函数本质上已经不是原函数(比如上例func的__name__已经不是func,而是inner,因为实际上,现在func是通过inner内部实现的)
- 解决办法:
- 在装饰器内的内部函数(inner)定义时加上一个特殊的装饰器 functools.wraps(func)
- 备注:实际上还是在inner的实例中调用func,但是只是把inner实例的__name__属性强行变为func
装饰器的作用--总结
- AOP面向切面编程
- 将业务实现和其他细节隔离开
- 切面就是业务实现
- 其他相关细节(酱料等)就是关注点
- 比如计时、记录日志、事务处理
- 分离代码逻辑
- 说白了就是将函数的主要逻辑代码和辅助功能的代码分离开,实际执行的时候通过装饰器将函数主要逻辑代码和辅助功能代码进行结合,这时候就要重新规定装饰后代码的返回值,因为最后的返回值是闭包函数执行后的返回结果