简析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 相关文章推荐
Python socket网络编程TCP/IP服务器与客户端通信
Jan 05 Python
python实现简单日期工具类
Apr 24 Python
详解用python写一个抽奖程序
May 10 Python
对Python _取log的几种方式小结
Jul 25 Python
python django 原生sql 获取数据的例子
Aug 14 Python
浅谈Django2.0 加xadmin踩的坑
Nov 15 Python
tensorflow之获取tensor的shape作为max_pool的ksize实例
Jan 04 Python
Python基础之列表常见操作经典实例详解
Feb 26 Python
python+selenium爬取微博热搜存入Mysql的实现方法
Jan 27 Python
Python .py生成.pyd文件并打包.exe 的注意事项说明
Mar 04 Python
在前女友婚礼上,用Python破解了现场的WIFI还把名称改成了
May 28 Python
用python画城市轮播地图
May 28 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
PHP 和 XML: 使用expat函数(二)
2006/10/09 PHP
php adodb连接mssql解决乱码问题
2009/06/12 PHP
php守护进程 加linux命令nohup实现任务每秒执行一次
2011/07/04 PHP
PHP中time(),date(),mktime()区别介绍
2013/09/28 PHP
Codeigniter+PHPExcel实现导出数据到Excel文件
2014/06/12 PHP
ThinkPHP在Cli模式下使用模板引擎的方法
2015/09/25 PHP
PHP new static 和 new self详解
2017/02/19 PHP
详解如何实现Laravel的服务容器的方法示例
2019/04/15 PHP
根据分辨率不同,调用不同的css文件
2006/08/25 Javascript
JS代码格式化和语法着色V2
2006/10/14 Javascript
Javascript模块模式分析
2008/05/16 Javascript
JQuery为textarea添加maxlength属性的代码
2010/04/07 Javascript
IE图片缓存document.execCommand(&quot;BackgroundImageCache&quot;,false,true)
2011/03/01 Javascript
原生Javascript封装的一个AJAX函数分享
2014/10/11 Javascript
jQuery的观察者模式详解
2014/12/22 Javascript
微信小程序实战之自定义抽屉菜单(7)
2017/04/18 Javascript
angularjs实现简单的购物车功能
2017/09/21 Javascript
Vue-cropper 图片裁剪的基本原理及思路讲解
2018/04/17 Javascript
微信小程序项目实践之九宫格实现及item跳转功能
2018/07/19 Javascript
使用imba.io框架得到比 vue 快50倍的性能基准
2019/06/17 Javascript
开始着手第一个Django项目
2015/07/15 Python
python中OrderedDict的使用方法详解
2017/05/05 Python
浅析Git版本控制器使用
2017/12/10 Python
Python matplotlib 画图窗口显示到gui或者控制台的实例
2018/05/24 Python
详解flask入门模板引擎
2018/07/18 Python
Python socket非阻塞模块应用示例
2019/09/12 Python
python多线程爬取西刺代理的示例代码
2021/01/30 Python
印尼最大的在线购物网站:MatahariMall.com
2016/08/26 全球购物
环境工程毕业生自荐信
2013/11/17 职场文书
办公室主任先进事迹
2014/01/18 职场文书
社团活动总结报告
2014/06/27 职场文书
2014年精神文明建设工作总结
2014/11/19 职场文书
党委工作总结2015
2015/04/27 职场文书
企业愿景口号
2015/12/25 职场文书
Python爬虫基础之简单说一下scrapy的框架结构
2021/06/26 Python
基于angular实现树形二级表格
2021/10/16 Javascript