#Python笔记# —— 命名空间(转)

浏览: 1740

Python命名空间、作用域一直没看懂。
这篇文是网上看到的,写的很好。
动手再梳理了下,然后再 将内容全部手打了一遍,增加理解。

原文:Python命名空间的本质 - windlaughing - 博客园 (http://www.cnblogs.com/windlaughing/archive/2013/05/26/3100362.html )

image.png

1、定义

Namespace是名字到对象的一个映射。绝大部分是dict形式,它的见健是那些变量值,值是那些变量的值。

A namespaceis a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries。
从某种那意义上来说,一个对象(object)的所有属性(attribute)也构成了一个namespace。在程序执行之间,会有不同的namespace同时存在。对于不同的命名空间内,相同名字的两个变量,这两个变量没有任何关系(互不影响)。

Python程序中,存在几个可用的命名空间:

局部命名空间:函数的命名空间,记录函数的变量,包括函数的参数和局部定义的变量

全局命名空间:模块的命名空间,记录模块的变量,包括函数、类、其它导入的模块、模块级变量和常量

内置命名空间:任何模块皆可访问,放置着内部函数和异常

2、查找顺序

一行代码要使用一个变量的名字时,会从所有命名空间去寻找,查找顺序如下:

1)一般情况

局部命名空间,全局命名空间,内置命名空间,若在这些命名空间找不到相关变量,Python会放弃查找并且引发一个NameError异常

2)嵌套函数

现在当前函数(嵌套或者lambda)的命名空间中查找,然后再父类函数命名空间中查找,接着在模块命名空间中查找,最后在内置命名空间中查找。

实例:

info="Adress:"
def func_father(country):
def func_son(area):
city="Chengdu"
print (info+country+city+area)
city="Beijing" # 此处的city变量,覆盖了父类的city变量
#调用内部函数
func_son("Wuhouqu")
func_father("China")

输出:

Adress:China Chengdu Wuhouqu

以上实例中,info在全局命名空间中,country在父类函数的命名空间中,city、area在自己函数的命名空间中

3、生命周期

不同Namespace的创建\销毁时间不同,就是说有不一样的生命周期。
1)内置命名空间在Python解释器启动时自动创建,会一直保留,不会删除
2)模块的全局命名空间在模块定义被读入时创建,通常一直保存到解释器退出
3)局部命名空间在函数被调用时创建,返回结果或抛出异常时被删除。所有递归调用的函数都有自己的命名空间

Python的一个特别之处是:

a、如果没有global关键字变量声明,一个变量的赋值总在最里层的作用域(innermost)
b、赋值不会复制数据,只是将命名绑定到对象。删除也是如此,"del y"只是从局部命名空间里删除命名y。(事实上,所有引入新命名的操作都作用于局部作用域)
c、赋值语句通常会隐式创建一个局部变量,即便在上一层作用域中已经存在该变量的赋值语句

实例1:

i=2
def func2():
i=i+1


func2()
#错误:UnbondLocalError:local variable 'i' referenced before assignment

由上,Python解释器执行到func3函数时,因为没有在局部作用域中找到对i的赋值(i=i+1无效),所以会抛出一个错误(原文:Python在检查代码前,发现了对i的赋值,并将其添加至局部命名空间中。执行时Python解释器会认为i在局部命名空间中但是没有值,所以产生错误)

def func3():
y=123
del y
print y

func3()
#错误:UnbondLocalError:local variable 'y' referenced before assignment
#取掉del y后运行正常

4、命名空间访问

1)局部命名空间可以用locals() BIF访问
locals()返回一个键/值的dict,键为变量名字(str形式),值为该变量的实际值
实例:

def func1(i,str):
x=12345
print (locals())

func1(1,'first')

输出:

{'str':'first','x':'12345','i':'1'}

2)全局(模块级别)命名空间可以用globals()BIF访问
实例:

'''Created on 2013-5-26'''

import copy
from copy import deepcopy

gstr = "global string"

def func1(i, info):
x = 12345
print(locals())

func1(1 , "first")

if __name__ == "__main__":
print("the current scope's global variables:")
dictionary=globals()
print(dictionary)

输出:

image.png

注意:

a、模块的命名空间不仅包括模块的常量和变量,还包括模块中定义的函数和类。此外还包括,任何被导入模块中的东西。
b、内置命名同样放置在一个模块中,被称作builtins
c、from module import 和import module不同:
使用import module,模块被导入,但是它仍保持自己的命名空间,所以需要模块名来访问函数或者属性(例如module.function);使用from module import function ,实际上是直接从另一个模块中导入相关属性或函数,因此可以直接访问而不需要知道它们的来源。使用globals函数可以实现。

3)locals()和globals()区别
locals是只读的(不能改变),globals不是(可以改变)
实例:

def func1(i,info):
x=12345
print locals()
locals()['x']='6789'
print 'x=',x

y=54321
func1('1','first')
globals()['y']='9876'
print 'y=',y

输出:

{‘info’:'first','x':'12345','i':'1'}
x=12345
y=9876

注意:

a、locals没有返回局部命名空间,它返回的是一个拷贝。所以对它进行改变,对局部命名空间中的变量值没有影响
b、globals返回全部实际命名空间,而非拷贝。所以globals返回的dict任何改动都会影响到全局变量(本例中后面的y覆盖掉了前面的y)

作用域、拷贝之类还没看,后面这一个有一些不太理解,先大概这样。

推荐 1
本文由 waking_up88688 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册