深入理解python中的闭包和装饰器


Posted in Python onJune 12, 2016

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

以下说明主要针对 python2.7,其他版本可能存在差异。

也许直接看定义并不太能明白,下面我们先来看一下什么叫做内部函数:

def wai_hanshu(canshu_1):

  def nei_hanshu(canshu_2): # 我在函数内部有定义了一个函数
    return canshu_1*canshu_2

  return nei_hanshu  # 我将内部函数返回出去

a = wai_hanshu(123)   # 此时 canshu_1 = 123
print a
print a(321)  # canshu_2 = 321

深入理解python中的闭包和装饰器

我在函数里面有嵌套了一个函数,当我向外层函数传递一变量的之后,并赋值给 a ,我们发现 a 变成了一个函数对象,而我再次为这个函数对象传参的时候,又获得了内部函数的返回值。我们知道,按照作用域的原则来说,我们在全局作用域是不能访问局部作用域的。但是,这里通过讨巧的方法访问到了内部函数。。

下面我们继续看一个例子:

def wai_hanshu():
  a = []
  def nei_hanshu(canshu):
    a.append(canshu)
    return a

  return nei_hanshu

a = wai_hanshu()
print a(123)
print a(321)

深入理解python中的闭包和装饰器

可以看出函数位于外部函数中的列表 a 竟然改变了。要知道为什么,就要先知道什么是python的命名空间,而命名空间就是作用域表现的原因,这里我简要说明一下。

引入命名空间的主要原因还是为了避免变量冲突,因为python中的模块众多,模块中又有函数,类等,它们都要使用到变量。但如果每次都要注意不和其他变量名冲突,那就太麻烦了,开发人员应该专注于自己的问题,而不是考虑别人写的程序中用到了什么变量,所以python引入了命名空间。命名空间分为模块层,模块内又分为全局作用域和局部作用域,用一个图来表示的话:

深入理解python中的闭包和装饰器

模块之间命名空间不同,而里面还有全局作用域和局部作用域,局部作用域之前还能嵌套,这样就能保证变量名不冲突了。这里顺便补充一下,可以通过 __name__ 属性获取命名空间的名字:

深入理解python中的闭包和装饰器

主文件的命名空间是叫做 '__main__',而模块的命名空间就是模块名。

作用域的诞生,是因为当python在寻找一个变量的时候,首先会在当前的命名空间中寻找,如果当前命名空间中没有,就到上一级的命名空间中找,以此类推,如果最后都没找到,则触发变量没找到的异常。

我们之前一直说:全局作用域无法访问局部作用域,而局部作用域能够访问全局作用域就这这个原因。而当我在局部作用域创建了一个和外面同名的变量时,python在找这个变量的时候首先会在当前作用域中找,找到了,就不继续往上一级找了。

在早期的python版本时,局部作用域是不能访问其他的局部作用域的,只能访问全局的,而现在的版本都是依次向上一级找,这里就提一下。

也就是因为这个特性,我们可以在内部函数中访问外部函数中的变量,这也就是所谓的闭包了。

注意:这里要做好对象之间的区分,例如:

def wai_hanshu():
  a = []
  def nei_hanshu(canshu):
    a.append(canshu)
    return a

  return nei_hanshu

a = wai_hanshu()  # 我创建了一个对象
b = wai_hanshu()  # 我又创建了一个对象
print a
print b
print a(123)
print b(321)

深入理解python中的闭包和装饰器

在这里,我们虽然都是操作 wai_hanshu 中的变量,但是 a 和 b 完全是两个对象,它们所在的内存空间也是不同的,所以里面的数据也是独立的。要注意不要搞混。

装饰器

其实装饰器就是在闭包的基础上多进行了几步,看代码:

def zsq(func): # 装饰函数
  def nei():
    print '我在传入的函数执行之前做一些操作'
    func() # 执行函数
    print '我在目标函数执行后再做一些事情'
  return nei

def login():  # 被装饰函数
  print '我进行了登录功能'

login = zsq(login)  # 我将被装饰的函数传入装饰函数中,并覆盖了原函数的入口

login()   # 此时执行的就是被装饰后的函数了

深入理解python中的闭包和装饰器

在看这段代码的时候,要知道几件事:

1.函数的参数传递的其实是引用,而不是值。

2.函数名也是一个变量,所以可以重新赋值。

3.赋值操作的时候,先执行等号右边的。

只有明白了上面这些事之后,再结合一下代码,应该就能明白什么是装饰器了。所谓装饰器就是在闭包的基础上传递了一个函数,然后覆盖原来函数的执行入口,以后调用这个函数的时候,就可以额外实现一些功能了。装饰器的存在主要是为了不修改原函数的代码,也不修改其他调用这个函数的代码,就能实现功能的拓展。

而python觉得让你每次都进行重命名操作实在太不方便,于是就给出了一个便利的写法:

def zsq(func):
  def nei():
    print '我在传入的函数执行之前做一些操作'
    func() # 执行函数
    print '我在目标函数执行后再做一些事情'
  return nei

@zsq  # 自动将其下面的函数作为参数传到装饰函数中去
def login():
  print '我进行了登录功能'


login()

深入理解python中的闭包和装饰器

这些小便利也叫做python的语法糖,你可能在很多地方见过这个说法。

带参数的装饰器:

def zsq(a):
  print '我是装饰器的参数', a
  def nei(func):
    print '我在传入的函数执行之前做一些操作'
    func() # 执行函数
    print '我在目标函数执行后再做一些事情'
  return nei


@zsq('123')
def login():
  print '我进行了登录功能'

深入理解python中的闭包和装饰器

相当于: login = zsq(123)(login) ,所以在这里没有调用就执行了。

装饰器的嵌套:

这里就不完整写个例子了:

@deco1(deco_arg) 
@deco2 
def func(): 
  pass

相当于: func = deco1(deco_arg)(deco2(func)) 

也就是从上到下的嵌套了。

关于闭包和装饰器就先讲到这里,以后有需要再补充。

以上这篇深入理解python中的闭包和装饰器就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现dnspod自动更新dns解析的方法
Feb 14 Python
Python3学习urllib的使用方法示例
Nov 29 Python
django连接mysql配置方法总结(推荐)
Aug 18 Python
python科学计算之numpy——ufunc函数用法
Nov 25 Python
Python小程序之在图片上加入数字的代码
Nov 26 Python
python创建ArcGIS shape文件的实现
Dec 06 Python
Python爬取腾讯视频评论的思路详解
Dec 19 Python
记一次django内存异常排查及解决方法
Aug 07 Python
通过代码实例解析Pytest运行流程
Aug 20 Python
python软件测试Jmeter性能测试JDBC Request(结合数据库)的使用详解
Jan 26 Python
python flask开发的简单基金查询工具
Jun 02 Python
详解Python内置模块Collections
Mar 22 Python
Python编码爬坑指南(必看)
Jun 10 #Python
浅析Python中的for 循环
Jun 09 #Python
Python多层嵌套list的递归处理方法(推荐)
Jun 08 #Python
Python-嵌套列表list的全面解析
Jun 08 #Python
PYTHON压平嵌套列表的简单实现
Jun 08 #Python
Python用Bottle轻量级框架进行Web开发
Jun 08 #Python
浅谈Python数据类型之间的转换
Jun 08 #Python
You might like
php数据库抽象层 PDO
2011/05/07 PHP
PHP中设置时区方法小结
2012/06/03 PHP
PHP获取指定月份第一天和最后一天的方法
2015/07/18 PHP
jQuery 菜单随滚条改为以定位方式(固定要浏览器顶部)
2012/05/24 Javascript
javascript学习笔记(八) js内置对象
2012/06/19 Javascript
js特殊字符转义介绍
2013/11/05 Javascript
jQuery使用addClass()方法给元素添加多个class样式
2015/03/26 Javascript
jquery简单实现外部链接用新窗口打开的方法
2015/05/30 Javascript
JS闭包、作用域链、垃圾回收、内存泄露相关知识小结
2016/05/16 Javascript
完美解决IE9浏览器出现的对象未定义问题
2016/09/29 Javascript
JS仿京东移动端手指拨动切换轮播图效果
2020/04/10 Javascript
详谈js中window.location.search的用法和作用
2017/02/13 Javascript
javascript中mouseenter与mouseover的异同
2017/06/06 Javascript
jQuery实现的弹幕效果完整实例
2017/09/06 jQuery
springmvc接收jquery提交的数组数据代码分享
2017/10/28 jQuery
基于node.js实现微信支付退款功能
2017/12/19 Javascript
[01:15:00]LGD vs Mineski Supermajor 胜者组 BO3 第一场 6.5
2018/06/06 DOTA
[01:27:30]LGD vs Newbee 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
Python threading多线程编程实例
2014/09/18 Python
python中threading超线程用法实例分析
2015/05/16 Python
利用Python开发实现简单的记事本
2016/11/15 Python
python中subprocess批量执行linux命令
2018/04/27 Python
pytz格式化北京时间多出6分钟问题的解决方法
2019/06/21 Python
Python hashlib模块加密过程解析
2019/11/05 Python
HTML5中5个简单实用的API
2014/04/28 HTML / CSS
美国顶尖折扣时尚购物网:Bluefly
2016/08/28 全球购物
美国领先的商务贺卡出版商:The Gallery Collection
2018/02/13 全球购物
高清安全摄像头系统:Lorex Technology
2018/07/20 全球购物
巴西Bo.Bô官方在线商店:经营奢侈品时尚业务
2020/03/16 全球购物
车间操作工岗位职责
2013/12/19 职场文书
《小石潭记》教学反思
2014/02/13 职场文书
机关出纳岗位职责
2014/04/03 职场文书
2014年体检中心工作总结
2014/12/23 职场文书
房屋认购协议书
2015/01/29 职场文书
2015年医院科室工作总结范文
2015/05/26 职场文书
Mysql事务索引知识汇总
2022/03/17 MySQL