对于初学者而言,闭包的概念一直都是很难理解的。
我在学习的时候,网上查了那么多资料,依然是一知半解。
直到后来我看了这篇文章:http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/
这是一篇很好的文章,由浅入深的告诉你到底闭包,装饰器都是什么东西。
E文不过关?没关系,我来用我自己的理解和你们聊聊命名空间、闭包和装饰器的那些事儿。
前提:
阅读这篇文章的前提,是你要对Python,有基础的理解,如果你有兴趣,但是对python还没有理解,我推荐你去读下面的教程:
www.liaoxuefeng.com
同时,微信公众号:easypython,中文名:说人话的python分享,也是很不错的教程,建议有一定入门基础的同学尝试。
说人话的python实战计划:http://study.163.com/course/introduction/1002794001.htm
开始:
命名空间
命名空间(namespace)这个概念,有时也被称为“作用域”,在大多数讲解闭包的时候都会有所涉及。
那什么是命名空间/作用域呢?
这两个概念,是用来描述一个变量的属性的,它们的意思,就是字面意思:“这个变量起名字的地方/这个变量起作用的域”.
我们用实例来说明一下:
def func(): x = 1
简单的函数,这个函数定义了一个叫做x的变量,且给了它一个值:1
那么,如果我们print这个变量呢?(我用注释来表示输出)
def func(): x = 1 print(x) func() # 1
好像完全是废话嘛!print(x)不显示1,难道还显示别的?
那好,我们换一种情况,如果我们在函数体外面print(x)呢?
def func(): x = 1 print(x) # Traceback 后面一大堆我省略了... # NameError: name 'x' is not defined
报了一个错误,错误的类型是name error,名称错误,错误的细节是:x没有定义。
看到这里,细心的朋友应该就知道我说这么多废话是什么意思啦!
x定义在函数体内部嘛,它的“作用范围”就仅仅是函数体内才能用!
你想把X打印出来?想把x加1?想把x乘以2?对不起,请到函数体内部去执行,外面,python不认识它。
说到python不认识它,其实是指“python的解释器”不认识它,解释器具体不多解释,你可以简单的理解为你电脑的python程序。
好吧好吧,python不认识它,那么如果,我换一种情况呢?我把两个语句交换一下位置?
x = 1 def func(): print(x) func() # 1
咦?你这个骗子,你不是说python不认识它吗?现在x不在函数体内定义了(事实上函数体内什么定义都没有~)
你怎么又认识它了?
问到这个问题,就得说一说python程序,读取一个变量的方法了。
简单的说,四个字:由内而外。
python先去查函数体内部:
1.我找到了,打印!
2.我找不到,我去找外面,找到了,打印!
3.我找不到,我去找外面,我还是找不到! NameError: 我找不到变量,你丫没定义吧?
看,就是这么个顺序。
那么找不到的x变量去哪里了?
比如这个程序:
def func(): x = 1 print(x)
x呢?x去哪里了?
答案是x被python解释器给“回收”了,所谓回收,你可以简单的理解成删除了、杀死了。
这里要引入一个概念,就是变量的“存活时间”
我写一下你就了解了(为了方便的标记函数体的结束,我写了一个返回字符串‘done’的return语句):
def func(): x = 1 # x的存活时间开始,在x=1这行下面操作x才可以! return 'Done' # 函数体结束,x被python杀死了。
就是如上代码中所表述的意思:
一个函数体内变量的存活时间,从定义它的那一刻开始,直到函数体结束为止。
我们可以通过一个很方便的、python给定我们的方法,来判断函数体内部还“活着”的变量有哪些,这个方法是locals()
我们看一个代码:
def func(): a = '哎呀' b = '我们' c = '还活着' print(locals()) func() # {'a': '哎呀', 'c': '还活着', 'b': '我们'}
看,locals()其实是一个字典,他列出了所有的“变量:变量值”这样的“键-值对”,而且字典是无序的。
locals()里面存的全部都是当前“活着”的变量,我们看另外一个例子:
def func(): a = '哎呀' b = '我们' c = '还活着' print(locals()) d = '我还没出生' func() # {'b': '我们', 'c': '还活着', 'a': '哎呀'}
看,locals()里面就没有变量d什么事情了,因为d在那个时候“还没出生”呢。
那么外部的变量呢?
x = 1 def func(): a = '内部' print(locals()) print(x) func() # {'a': '内部'} # 1
看到了吧,内部变量只有一个,locals也打印出来了,但是x还是正常的可以打印。
因为python在内部找不到,于是就去外面找了,外部的变量,可以用globals()检查到,但是这个很长…………
x = 1 def func(): a = '内部' print(locals()) print(globals()) func() # {'a': '内部'} # ....(一大堆我就不粘贴了)..... 'x': 1}
命名空间/作用域的概念就是这样,它决定了python程序“去哪里”找到变量。