简析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中的type()方法的使用
May 21 Python
python3+PyQt5实现自定义流体混合窗口部件
Apr 24 Python
python使用RNN实现文本分类
May 24 Python
如何在Python中实现goto语句的方法
May 18 Python
python多线程同步实例教程
Aug 11 Python
Python3 翻转二叉树的实现
Sep 30 Python
Python3和pyqt5实现控件数据动态显示方式
Dec 13 Python
Python+OpenCV实现旋转文本校正方式
Jan 09 Python
Python numpy多维数组实现原理详解
Mar 10 Python
django从后台返回html代码的实例
Mar 11 Python
Python读取文件夹下的所有文件实例代码
Apr 02 Python
Django集成富文本编辑器summernote的实现步骤
May 31 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
如何阻止网站被恶意反向代理访问(防网站镜像)
2014/03/18 PHP
Yii学习总结之数据访问对象 (DAO)
2015/02/22 PHP
简单了解WordPress开发中update_option()函数的用法
2016/01/11 PHP
php获取ajax的headers方法与内容实例
2017/12/27 PHP
JQuery Tips(4) 一些关于提高JQuery性能的Tips
2009/12/19 Javascript
Javascript 中的 call 和 apply使用介绍
2012/02/22 Javascript
jquery选择checked在ie8普通模式下的问题
2014/02/12 Javascript
jquery获取checkbox的值并post提交
2015/01/14 Javascript
JS实用的动画弹出层效果实例
2015/05/05 Javascript
Jquery实现瀑布流布局(备有详细注释)
2015/07/31 Javascript
js实现二级导航功能
2017/03/03 Javascript
validationEngine 表单验证插件使用实例代码
2017/06/15 Javascript
BootStrap给table表格的每一行添加一个按钮事件
2017/09/07 Javascript
node.js 发布订阅模式的实例
2017/09/10 Javascript
vue-cli项目中怎么使用mock数据
2017/09/27 Javascript
代码详解javascript模块加载器
2018/03/04 Javascript
vue多级复杂列表展开/折叠及全选/分组全选实现
2018/11/05 Javascript
D3.js的基础部分之数组的处理数组的排序和求值(v3版本)
2019/05/09 Javascript
vue组件创建的三种方式小结
2020/02/03 Javascript
Python实现批量转换文件编码的方法
2015/07/28 Python
python生成二维码的实例详解
2017/10/29 Python
Pycharm更换python解释器的方法
2018/10/29 Python
在scrapy中使用phantomJS实现异步爬取的方法
2018/12/17 Python
Django框架模板文件使用及模板文件加载顺序分析
2019/05/23 Python
tensor和numpy的互相转换的实现示例
2019/08/02 Python
pandas 对group进行聚合的例子
2019/12/27 Python
10个python3常用排序算法详细说明与实例(快速排序,冒泡排序,桶排序,基数排序,堆排序,希尔排序,归并排序,计数排序)
2020/03/17 Python
英国休闲奢华的缩影:Crew Clothing
2019/05/05 全球购物
英国第一职业高尔夫商店:Clickgolf.co.uk
2020/11/18 全球购物
物流管理专业推荐信
2014/09/06 职场文书
2015年大学生实习评语
2015/03/25 职场文书
高中开学感言
2015/08/01 职场文书
《圆的面积》教学反思
2016/02/19 职场文书
简短的36句中秋节祝福信息语句
2019/09/09 职场文书
一篇文章学会Vue中间件管道
2021/06/20 Vue.js
介绍一下28个JS常用数组方法
2022/05/06 Javascript