Python基础(十三)
2019-10-02 09:06
1406 查看
今日主要内容
- 闭包
- 装饰器初识
- 标准装饰器
一、闭包
(一)什么是闭包
闭包:内层函数调用外层函数的变量就是闭包(不能是全局变量)
def func1(): a = 10 def func2(): print(a) # 内层函数调用外层函数的变量,这就是一个闭包 func1()
检测闭包的方法
函数名.__closure__
- 若返回对象地址就是一个闭包,返回None就不是一个闭包
- 注意:
.__closure__
检测的是函数名,判断这个函数是否是闭包 如何在全局空间中调用内部函数
-
外层函数返回内层函数的函数名(内存地址),就可在全局空间中调用内部函数
def func1(): a = 10 def func2(): print(a) # 调用外层变量a,是闭包 print(func2.__closure__) # 判断func2是否是闭包 func1() 运行结果: (<cell at 0x00000230F21874F8: int object at 0x0000000063258190>,)
def func1(): a = 10 def func2(): a = 10 print(a) # 使用自身函数变量a,不是闭包 print(func2.__closure__) func1() 运行结果: None
def func1(): a = 10 def func2(): print(a) return func2 # 返回内层函数的函数名 f = func1() # 此时f就是func2 f() # 调用内层函数func2 print(f.__closure__) 运行结果: 10 (<cell at 0x00000280A87574F8: int object at 0x0000000063258190>,)
(二)闭包的作用
- 保护数据安全
- 说白话一点,如果数据放在全局变量中(顶格写代码),数据的安全性很低,因为所有人都可以无意间修改它,想解决这个问题我们可以把数据放到函数中,只要不调用这个函数,我的数据就是安全的。但是有个问题,每次执行函数完,解释器就会自动清空此函数开辟的局部空间中所有内容(包括数据开辟的空间),所以每次调用完函数,我的数据就没了,此时就需要利用到了闭包。闭包可以在内层函数调用外层函数中的变量,而且内层函数如果调用了外层函数中的变量,那这个变量将不会消亡,将会常驻内存。所以,我们只需要在全局空间中调用这个闭包的内层函数,就可以使用我的数据了,而且数据的安全性也提高了。
将变量常驻内存,供后续代码使用
def outer(): lst = [1,2,3,4,5] def inner(): return lst return inner f = outer() f_lst = f() print(f_lst) 运行结果: [1,2,3,4,5]
(三)闭包的应用
- 防止数据被误修改
- 装饰器(与闭包格式相同)
二、装饰器初识
(一)软件开发的六大原则(了解)
- 开闭原则(Open Close Principle)——装饰器依据
- 里氏代换原则(Liskov Substitution Principle)
- 依赖倒转原则(Dependence Inversion Principle)
- 接口隔离原则(Interface Segregation Principle)
- 迪米特法则(最少知道原则)(Demeter Principle)
- 合成复用原则(Composite Reuse Principle)
(二)装饰器依据——开闭原则
- 开放封闭原则: 对功能扩展开放
- 对源码修改封闭
(三)装饰器引入
相信大多数人都玩LOL,我们模拟一次游戏过程:
向平常一样的流程,十连跪...
def play_lol(): print("登陆游戏") print("开始排位...") print("游戏中...") print("失败...") print("结束游戏") play_lol() 运行结果: 登陆游戏 开始排位... 游戏中... Virtory 结束游戏
- 受不了了,开一个外挂吧,此时我的函数需要扩展,在前后添加开启关闭外挂的功能
def play_lol(): print("开启外挂!") # 添加开启外挂功能 print("登陆游戏") print("开始排位...") print("游戏中...") print("胜利!!!") print("结束游戏") print("关闭外挂!") # 添加关闭外挂功能 play_lol() 运行结果: 开启外挂! 登陆游戏 开始排位... 游戏中... 胜利!!! 结束游戏 关闭外挂!
- 但此时,违背了开闭原则,对源代码进行了修改。想一个方法,对源代码进行前后包装,不改变源码
def play_lol(): print("登陆游戏") print("开始排位...") print("游戏中...") print("胜利!!!") print("结束游戏") def new_play(): # 将上面代码前后包装成了一个新的函数,没有改变源码 print("开启外挂!") play_lol() print("关闭外挂!") new_play() 运行结果: 开启外挂! 登陆游戏 开始排位... 游戏中... 胜利!!! 结束游戏 关闭外挂!
- 功能实现了,而且还没有改变源码,但是有一个问题,我们之前访问调用的是
play_lol
这个函数,但此时我们访问调用的是new_play()
这个函数,相当于改变了调用,还是违背了开闭原则,没有达到扩展的效果,此时我们就需要对这段代码稍作变化
def play_lol(): print("登陆游戏") print("开始排位...") print("游戏中...") print("胜利!!!") print("结束游戏") def wrapper(fn): # 装饰器雏形 def inner(): print("开启外挂!") fn() print("关闭外挂!") return inner func = wrapper(play_lol) # 调用装饰器函数将我基础函数传入进去包装 play_lol = func # 返回的是包装函数,将包装函数重命名成我原来的函数 play_lol() # 调用此函数 运行结果: 开启外挂! 登陆游戏 开始排位... 游戏中... 胜利!!! 结束游戏 关闭外挂!
- 上述代码就引出了装饰器的雏形,刨析一下: 装饰器雏形:与闭包的格式相同,两层函数构成: 内层函数就是我的包装函数,将扩展的功能和原函数包在一起组成一个函数
- 外层函数的作用就是给内层函数传参用的,传入的是我原函数的函数名,在内层调用
return inner:装饰器的返回值是内层函数的函数名,真正进行包装扩展的是内层函数
def wrapper(fn): # 装饰器雏形 def inner(): print("开启外挂!") fn() print("关闭外挂!") return inner func = wrapper(play_lol) # 调用装饰器函数将我基础函数传入进去包装 play_lol = func # 返回的是包装函数,将包赚函数重命名成我原来的函数 play_lol() # 调用此函数
利用语法糖装饰
使用装饰器的两行代码可以转换成语法糖
func = wrapper(play_lol) # 调用装饰器函数将我基础函数传入进去包装 play_lol = func # 返回的是包装函数,将包赚函数重命名成我原来的函数
- 语法糖
@wrapper # 语法糖 def play_lol(): print("登陆游戏") print("开始排位...") print("游戏中...") print("胜利!!!") print("结束游戏")
此时,装饰器的雏形就出来了
装饰器雏形
def wrapper(fn): def inner(): """扩展功能""" fn() """扩展功能""" return inner @wrapper def func(): pass func()
游戏模拟继续进行
-
就算开挂,我们也得选完英雄,才能进入游戏,所以我们给基础函数传个参数,但是我们真正执行的是装饰器内部的inner包装函数,所以也要给inner传入参数
def wrapper(fn): # 装饰器雏形 def inner(hero): # 套到装饰器中内层包装函数参数 print("开启外挂!") fn(hero) # 基础函数参数 print("关闭外挂!") return inner @wrapper def play_lol(hero): # 基础函数参数 print("登陆游戏") print("开始排位...") print(f"选择英雄:{hero}") print("游戏中...") print("胜利!!!") print("结束游戏") play_lol("盖伦") 运行结果: 开启外挂! 登陆游戏 开始排位... 选择英雄:盖伦 # 传入的参数 游戏中... 胜利!!! 结束游戏 关闭外挂!
- 虽然开挂了,但是还是会遇到巨坑无敌坑的队友,我们得把他记下来举报他,所以要给基础函数填写返回值,同时给真正执行的inner函数填写返回值
def wrapp 576 er(fn): # 装饰器雏形 def inner(hero): print("开启外挂!") ret = fn(hero) # 接收基础函数的返回值 print("关闭外挂!") return ret # 返回包装后的函数的返回值 return inner @wrapper def play_lol(hero): # 基础函数参数 print("登陆游戏") print("开始排位...") print(f"选择英雄:{hero}") print("游戏中...") print("胜利!!!") print("结束游戏") return "坑比队友:xxx" # 基础函数返回值 print(play_lol("盖伦")) 运行结果: 开启外挂! 登陆游戏 开始排位... 选择英雄:盖伦 游戏中... 胜利!!! 结束游戏 关闭外挂! 坑比队友:xxx # 返回值
- 到此,我们装饰器标准模式也就出来了
装饰器标准模式(非常重要)
def wrapper(fn): def inner(*args, **kwargs): """扩展功能""" ret = fn(*args, **kwargs) """扩展功能""" return ret return inner @wrapper def func(): pass func()
相关文章推荐
- Python基础<十三:版本差异>
- Python基础学习(十三)--第十三天
- Python学习笔记(十三):模块基础
- Python基础教程(十三):JSON、练习题100题
- python基础学习笔记(十三)
- python基础(十三) cmd命令调用
- python基础教程(十三)
- Python零基础入门十三之异常
- python_fullstack基础(十三)-异常处理
- Python基础入门(十三)-切片
- 基础知识(十三)dlib python人脸检测
- Python基础(十三) 为什么说python多线程没有真正实现多现程
- Py修行路 python基础 (十三)匿名函数 与 内置函数
- 【零基础】Python3学习课后练习题(十三)
- python基础类型
- 马哥2016全新Linux+Python高端运维班-Linux基础命令文件管理类及目录创建
- python基础知识-内置函数
- python基础之继承派生、组合、接口和抽象类
- Python学习第三章《字符串》(成为高手前必须知道的一些基础知识)
- Python基础教程 第8章: 异常 学习笔记