以下说明主要针对 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
我在函数里面有嵌套了一个函数,当我向外层函数传递一变量的之后,并赋值给 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)
可以看出函数位于外部函数中的列表 a 竟然改变了。要知道为什么,就要先知道什么是python的命名空间,而命名空间就是作用域表现的原因,这里我简要说明一下。
引入命名空间的主要原因还是为了避免变量冲突,因为python中的模块众多,模块中又有函数,类等,它们都要使用到变量。但如果每次都要注意不和其他变量名冲突,那就太麻烦了,开发人员应该专注于自己的问题,而不是考虑别人写的程序中用到了什么变量,所以python引入了命名空间。命名空间分为模块层,模块内又分为全局作用域和局部作用域,用一个图来表示的话:
模块之间命名空间不同,而里面还有全局作用域和局部作用域,局部作用域之前还能嵌套,这样就能保证变量名不冲突了。这里顺便补充一下,可以通过 __name__ 属性获取命名空间的名字:
主文件的命名空间是叫做 '__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)
在这里,我们虽然都是操作 wai_hanshu 中的变量,但是 a 和 b 完全是两个对象,它们所在的内存空间也是不同的,所以里面的数据也是独立的。要注意不要搞混。
装饰器
其实装饰器就是在闭包的基础上多进行了几步,看代码:
def zsq(func): # 装饰函数 def nei(): print '我在传入的函数执行之前做一些操作' func() # 执行函数 print '我在目标函数执行后再做一些事情' return nei def login(): # 被装饰函数 print '我进行了登录功能' login = zsq(login) # 我将被装饰的函数传入装饰函数中,并覆盖了原函数的入口 login() # 此时执行的就是被装饰后的函数了
在看这段代码的时候,要知道几件事:
1.函数的参数传递的其实是引用,而不是值。
2.函数名也是一个变量,所以可以重新赋值。
3.赋值操作的时候,先执行等号右边的。
只有明白了上面这些事之后,再结合一下代码,应该就能明白什么是装饰器了。所谓装饰器就是在闭包的基础上传递了一个函数,然后覆盖原来函数的执行入口,以后调用这个函数的时候,就可以额外实现一些功能了。装饰器的存在主要是为了不修改原函数的代码,也不修改其他调用这个函数的代码,就能实现功能的拓展。
而python觉得让你每次都进行重命名操作实在太不方便,于是就给出了一个便利的写法:
def zsq(func): def nei(): print '我在传入的函数执行之前做一些操作' func() # 执行函数 print '我在目标函数执行后再做一些事情' return nei @zsq # 自动将其下面的函数作为参数传到装饰函数中去 def login(): print '我进行了登录功能' login()
这些小便利也叫做python的语法糖,你可能在很多地方见过这个说法。
带参数的装饰器:
def zsq(a): print '我是装饰器的参数', a def nei(func): print '我在传入的函数执行之前做一些操作' func() # 执行函数 print '我在目标函数执行后再做一些事情' return nei @zsq('123') def login(): print '我进行了登录功能'
相当于: login = zsq(123)(login) ,所以在这里没有调用就执行了。
装饰器的嵌套:
这里就不完整写个例子了:
@deco1(deco_arg) @deco2 def func(): pass
相当于: func = deco1(deco_arg)(deco2(func))
也就是从上到下的嵌套了。
关于闭包和装饰器就先讲到这里,以后有需要再补充。
以上这篇深入理解python中的闭包和装饰器就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章推荐
• 【腾讯云】年中优惠,「专享618元」优惠券!• Python数据分析之concat与merge函数(实例详解)• 实例详解Python面向对象的四大特征• 图文详解怎么用Python绘制动态可视化图表• 简单学习Python字符和列表(实例详解)• 介绍六个超好用的Python内置函数独孤九贱(3)_JavaScript视频教程
javascript是运行在浏览器上的脚本语言,连续多年,被评为全球最受欢迎的编程语言。是前端开发必备三大法器中,最具杀伤力。如果前端开发是降龙十八掌,好么javascript就是第18掌:亢龙有悔。没有它,你的前端生涯是不完整的。《php.cn独孤九贱(3)-JavaScript视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了javascript知识。
JavaScript教程125171次播放
独孤九贱(6)_jQuery视频教程
jQuery是一个快速、简洁的JavaScript框架。设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的JavaScript设计模式,优化HTML文档操作、事件处理、动画设计和Ajax交互。 核心特性可以总结为:具有独特的链式语法和短小清晰的多功能接口;具有高效灵活的css选择器,并且可对CSS选择器进行扩展;拥有便捷的插件扩展机制和丰富的插件。兼容各种主流浏览器,如IE 6.0+、FF 1.5+、Safari 2.0+、Opera 9.0+等,是全球最流行的前端开发框架之一。PHP中文网根据最新版本,独家录制jQuery最新视频教程,回馈PHP中文网的新老用户。
jQuery教程103550次播放
jQuery与Ajax基础与实战
jQuery是最流行的JS函数库,封装了许多实用的功能,其中最引人入胜的就是Ajax。 jQuery中的Ajax操作,语法简单,操作方便,使Ajax从未如此轻松,前端人员从此不再为与服务器异步交互而发愁,本套课程,精选了最常用的几个方法,从基本的语法到每个参数,再到具体实例进行了全面的讲解。
AJAX教程4687次播放
Git教程(60分钟全程无废话版)
Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持
JavaScript教程4025次播放