最近一直看一本python经典教材——《Python学习手册》,因为之前都是突击学的,也没有仔细看一些经典教材,所以感觉自己的基础掌握的还不是很好,虽然网络上资源多,但我觉得还是有必要买本教材来认真的读一读,底层基础决定上层建筑嘛,基础打牢一些,对今后的编程还是会有些帮助的。

     今天来谈谈python中的命名空间和作用域相关的概念(其实很多面向对象程序语言都有这个概念,我记得自己当初自学C++时就接触过这两个概念),希望对有需要的朋友有帮助,当然我更希望你能找本教材来看看。

       命名空间是一个保存变量名的地方,当在程序中使用变量名时,python创建、改变或者查找都是在所谓的命名空间中进行的。当我们谈论到搜索变量名对应于代码的值的时候,作用域这个术语指的就是命名空间,也就是说在代码中,变量名被赋值的位置决定了这个变量名能被访问到的访问

        python的变量名在第一次赋值时就已经创建,并且必须经过赋值后才能使用,python会将一个变量名被赋值的地点关联为一个特点的命名空间(也就是说命名空间就是名字到实际python对象的一个映射,命名空间是一个 字典(dictionary) ,它的键就是变量名,它的值就是那些变量的值,我们知道python中一切皆对象,但变量名并不是对象,类型是属于对象,而不属于变量名),也就是说在代码中给一个变量赋值的地方决定了这个变量存在于哪个命名空间。

        函数定义了本地作用域,函数除了打包代码外,函数还为程序增加了一个额外的命名空间层:在默认情况下,一个函数的所有变量名都与函数命名空间相关联,这就意味着:一个def内定义的变量名能够被def内代码使用,不能在函数外部引用这样的变量名;def与def的变量名不发生冲突,两者相互独立,互不影响。

        模块定义的是全局作用域。本地作用域与全局作用域有如下关系:

.    内嵌的模块是全局作用域。每个模块都是一个全局作用域,对于外部的全局变量就变成了模块的属性

.    全局作用域的作用范围仅限于单个文件。这里的“全局”指在一个文件顶层的变量名仅对于这个文件内部的代码而言是全局的,在python中没有一个基于当个文件的、无所不包的情景文件的全局作用域。如果其它文件想引用某一个文件的全局变量,必须先导入该模块文件才能使用这个模块中定义的变量名

.    每次对函数调用都会创建了一个新的本地作用域。

.    赋值的变量名除非声明为全局变量,否则均为本地变量。在默认情况下,使用函数定义内部的变量名是位于本地作用域内的。如果想给一个在函数内部却位于模块文件顶层的变量嘛赋值(也就是说想在函数内部给全局变量名赋值)就需要在函数内部使用global语句声明,否则按照LEGB原则,函数内部会认为它是本地变量。如下:

#coding=utf-8
X = 99  #全局变量
def fun():
    X = 100   #由于没有用global声明,根据LEGB原则,函数认为它是本地变量
    return X

print X     
print fun()

#最后输出结果是99,100;并不是100,100;X值没有发生改变,是因为函数认为X是本地变量,
#如果想函数认为X是全局变量,用global声明。

.     所有变量名都可以归纳为本地,全局或者内置


那么,什么是“LEGB”呢?

"LEGB"是python中四层命名空间的英文名字首字母的缩写。
最里面的一层是L(local),表示在一个函数定义中,而且在这个函数里面没有再包含函数的定义。
第二层E(enclosing function),表示在一个函数定义中,但这个函数里面还包含有函数的定义,其实L层和E层只是相对的。
第三层G(global),是指一个模块的命名空间,也就是说在一个.py文件中定义的标识符,但不在一个函数中。
第四层B(builtin),是指python解释器启动时就已经具有的命名空间,之所以叫builtin是因为在python解释器启动时会自动载入__builtin__模块,这个模块中的list、str等内置函数的就处于B层的命名空间中。

变量名引用分为三个作用域进行查找:首先是本地,之后是函数内(如果有的话),之后是全局,最后是内置


不同的命名空间在不同的时刻创建,有不同的生存期。

     1、内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。

     2、模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。

     3、当函数被调用时创建一个本地命名空间,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间。

下面来讲讲python的两个内置函数locals()  和 globals()
1.本地命名空间可以用locals()来访问

     locals 返回一个名字/值对的 dictionary。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。

#coding=utf-8
def func(i,y):
    x = 666
    print locals()
func(88,"Good")

输出结果是{'y': 'Good', 'x': 666, 'i': 88},

如果你知道这个技巧的话,在利用Django框架写视图函数时,就可以省很多事了,比如下面例子:

def def current_datetime(request):   
    now = datetime.datetime.now()   
    return render_to_response('current_datetime.html',
     {'current_date': now})

我们可以这样写:

def current_datetime(request):   
    current_date = datetime.datetime.now()   
    return render_to_response('current_datetime.html', locals())

当要返回给前段的值比较多时,这样写是不是很方便呢?


2.全局 (模块级别)命名空间可以通过 globals() 来访问

#coding=utf-8
 
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)

输出

{'info': 'first', 'x': 12345, 'i': 1}
the current scope's global variables:
{'func1': <function func1 at 0x7fca54379c08>,
'gstr': 'global string',
'dictionary': {...},
'__builtins__': <module '__builtin__' (built-in)>,
'__file__': '/home/liulonghua/test/12.py',
'__package__': None,
'deepcopy': <function deepcopy at 0x7fca54379758>,
'__name__': '__main__',
'copy': <module 'copy' from '/usr/lib/python2.7/copy.pyc'>,
'__doc__': None
}

3.locals 与 globals 之间的一个重要的区别
locals 是只读的,globals 不是
   locals 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行改变对局部名字空间中
的变量值并无影响。  
   globals 返回实际的全局名字空间,而不是一个拷贝。所以对 globals 所返回的 dictionary 的任何
的改动都会直接影响到全局变量。