简析Python的闭包和装饰器


Posted in Python onFebruary 26, 2016

什么是装饰器?
装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直接看示意图,其中 a 为与装饰器 @a 对应的函数, b 为装饰器修饰的函数,装饰器@a的作用是:

简析Python的闭包和装饰器

简而言之:@a 就是将 b 传递给 a(),并返回新的 b = a(b)

栗子:

简析Python的闭包和装饰器

上面使用@dobi来表示装饰器,其等同于:qinfeng = dobi(qinfeng)
因此装饰器本质上就是个语法糖,其作用为简化代码,以提高代码可读性,运行上段代码的结果为:

简析Python的闭包和装饰器

解析过程是这样子的:
1.python 解释器发现@dobi,就去调用与其对应的函数( dobi 函数)
2.dobi 函数调用前要指定一个参数,传入的就是@dobi下面修饰的函数,也就是 qinfeng()
3.dobi() 函数执行,调用 qinfeng(),qinfeng() 打印“dobi”

什么是闭包?
首先还得从基本概念说起,什么是闭包呢?来看下维基上的解释:
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
....
上面提到了两个关键的地方: 自由变量 和 函数, 这两个关键稍后再说。还是得在赘述下“闭包”的意思,望文知意,可以形象的把它理解为一个封闭的包裹,这个包裹就是一个函数,当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量,自由变量可以在随着包裹到处游荡。当然还得有个前提,这个包裹是被创建出来的。
在通过Python的语言介绍一下,一个闭包就是你调用了一个函数A,这个函数A返回了一个函数B给你。这个返回的函数B就叫做闭包。你在调用函数A的时候传递的参数就是自由变量。
举个栗子:

def func(name):
  def inner_func(age):
    print 'name:', name, 'age:', age
  return inner_func

bb = func('the5fire')
bb(26) # >>> name: the5fire age: 26

这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收。

另外再说一点,闭包并不是Python中特有的概念,所有把函数做为一等公民的语言均有闭包的概念。不过像Java这样以class为一等公民的语言中也可以使用闭包,只是它得用类或接口来实现。

nonlocal 语句
在 python 的函数内,可以直接引用外部变量,但不能改写外部变量,因此如果在闭包中直接改写父函数的变量,就会发生错误:

简析Python的闭包和装饰器

在 python 2 中可以在函数内使用 global 语句,但全局变量在任何语言中都不被提倡,因为它很难控制,python 3 中引入了 nonlocal 语句解决了这个问题:

简析Python的闭包和装饰器

Nonlocal 与 global 的区别在于 nonlocal 语句会去搜寻本地变量与全局变量之间的变量,其会优先寻找层级关系与闭包作用域最近的外部变量。

闭包与装饰器
上面已经简单演示了装饰器的功能,事实上,装饰器就是一种的闭包的应用,只不过其传递的是函数:

简析Python的闭包和装饰器

@makeitalic 装饰器将函数 hello 传递给函数 makeitalic,函数 makeitalic 执行完毕后返回被包装后的 hello 函数,而这个过程其实就是通过闭包实现的。@makebold 也是如此,只不过其传递的是 @makeitalic 装饰过的 hello 函数,因此最后的执行结果 <b> 在 <i> 外层,这个功能如果不用装饰器,其实就是显式的使用闭包:

简析Python的闭包和装饰器

闭包的作用
闭包的最大特点是可以将父函数的变量与内部函数绑定,并返回绑定变量后的函数(也即闭包),此时即便生成闭包的环境(父函数)已经释放,闭包仍然存在,这个过程很像类(父函数)生成实例(闭包),不同的是父函数只在调用时执行,执行完毕后其环境就会释放,而类则在文件执行时创建,一般程序执行完毕后作用域才释放,因此对一些需要重用的功能且不足以定义为类的行为,使用闭包会比使用类占用更少的资源,且更轻巧灵活,现举一例:假设我们仅仅想打印出各类动物的叫声,分别以类和闭包来实现:

简析Python的闭包和装饰器

可以看到输出结果是完全一样的,但显然类的实现相对繁琐,且这里只是想输出一下动物的叫声,定义一个 Animal 类未免小题大做,而且 voice 函数在执行完毕后,其作用域就已经释放,但 Animal 类及其实例 dog 的相应属性却一直贮存在内存中:

简析Python的闭包和装饰器

而这种占用对于实现该功能后,则是没有必要的。

除此之外,闭包还有很多其他功能,比如用于封装等,另外,闭包有效的减少了函数参数的数目,这对并行计算非常有价值,比如可以让每台电脑负责一个函数,然后串起来,实现流水化的作业等。

Python 相关文章推荐
精确查找PHP WEBSHELL木马的方法(1)
Apr 12 Python
python数据库操作常用功能使用详解(创建表/插入数据/获取数据)
Dec 06 Python
Python中的列表知识点汇总
Apr 14 Python
利用Fn.py库在Python中进行函数式编程
Apr 22 Python
tensorflow建立一个简单的神经网络的方法
Feb 10 Python
遗传算法python版
Mar 19 Python
python实现微信自动回复功能
Apr 11 Python
Python wxPython库使用wx.ListBox创建列表框示例
Sep 03 Python
详解Python读取yaml文件多层菜单
Mar 23 Python
python利用多种方式来统计词频(单词个数)
May 27 Python
python实现按关键字筛选日志文件
Dec 24 Python
Python数据可视化之用Matplotlib绘制常用图形
Jun 03 Python
Android应用开发中Action bar编写的入门教程
Feb 26 #Python
12步教你理解Python装饰器
Feb 25 #Python
Python实现字典依据value排序
Feb 24 #Python
Python中方法链的使用方法
Feb 23 #Python
python开发之list操作实例分析
Feb 22 #Python
python开发之str.format()用法实例分析
Feb 22 #Python
python文件与目录操作实例详解
Feb 22 #Python
You might like
Thinkphp页面跳转设置跳转等待时间的操作
2019/10/16 PHP
在一个js文件里远程调用jquery.js会在ie8下的一个奇怪问题
2010/11/28 Javascript
基于jQuery的倒计时实现代码
2012/05/30 Javascript
用JS将搜索的关键字高亮显示实现代码
2013/11/08 Javascript
jQuery 事件的命名空间简单了解
2013/11/22 Javascript
扩展IE中一些不兼容的方法如contains、startWith等等
2014/01/09 Javascript
JavaScript前端图片加载管理器imagepool使用详解
2014/12/29 Javascript
JavaScript数组常用方法
2015/03/02 Javascript
JQuery判断radio(单选框)是否选中和获取选中值方法总结
2015/04/15 Javascript
Bootstrap每天必学之级联下拉菜单
2016/03/27 Javascript
jQuery Easyui datagrid行内实现【添加】、【编辑】、【上移】、【下移】
2016/12/19 Javascript
使用jquery+iframe做一个ajax上传效果(实例)
2017/08/24 jQuery
Vue-Router模式和钩子的用法
2018/02/28 Javascript
vue数据操作之点击事件实现num加减功能示例
2019/01/19 Javascript
elementui之el-tebs浏览器卡死的问题和使用报错未注册问题
2019/07/06 Javascript
IntelliJ IDEA编辑器配置vue高亮显示
2019/09/26 Javascript
vue 实现走马灯效果
2019/10/28 Javascript
JS数组转字符串实现方法解析
2020/09/04 Javascript
用python 制作图片转pdf工具
2015/01/30 Python
Python中正则表达式的详细教程
2015/04/30 Python
对python PLT中的image和skimage处理图片方法详解
2019/01/10 Python
在Python中合并字典模块ChainMap的隐藏坑【推荐】
2019/06/27 Python
python监控进程状态,记录重启时间及进程号的实例
2019/07/15 Python
Numpy对数组的操作:创建、变形(升降维等)、计算、取值、复制、分割、合并
2019/08/28 Python
Python 实现判断图片格式并转换,将转换的图像存到生成的文件夹中
2020/01/13 Python
利用django创建一个简易的博客网站的示例
2020/09/29 Python
CSS3 rgb and rgba(透明色)的使用详解
2020/09/25 HTML / CSS
Rowdy Gentleman服装和配饰:美好时光
2019/09/24 全球购物
品恩科技软件测试面试题
2014/10/26 面试题
Delphi工程师笔试题
2013/09/21 面试题
年度安全生产目标责任书
2014/07/23 职场文书
员工辞职信怎么写
2015/02/27 职场文书
2015年党员创先争优公开承诺书
2015/04/27 职场文书
Pytorch 如何加速Dataloader提升数据读取速度
2021/05/28 Python
浅谈css实现背景颜色半透明的两种方法
2021/12/06 HTML / CSS
「月刊Comic Alive」2022年5月号封面公开
2022/03/21 日漫