Python 之作用域和名字空间

作用域与名字空间 

 

Python有一个核心概念是名字空间(namespace),namespace是一个name到object 的映射关系,Python有很多namespace,因此,在代码中如果碰到一个标志符(name),需要有一个规则来决定去哪个namespace查找——这就是LEGB。

LEGB决定了name的查找顺序:locals -> enclosing function -> globals -> __builtins__

  • locals 是函数内的名字空间,包括局部变量和形参;
  • enclosing 外部嵌套函数的名字空间(闭包中常见);
  • globals 全局变量,函数定义所在模块的名字空间;
  • builtins 内置模块的名字空间;

所以,在 Python 中检索一个变量的时候,优先到 locals 里面来检索,检索不到的情况下会检索 enclosing ,enclosing 没有则到 globals 全局变量里面检索,最后是到 builtins 里面来检索。

当然,因为 builtins 的特殊性,我们可以直接在 builtins 里面添加变量,这样就可以在任意模块中访问变量,不过这种方法太过于变态,不推荐这么做。

LGB规则(闭包是一种特殊的作用域,暂不考虑)与name space加载的顺序相反,Python解释器初始化的时候会先加载built-in namespace,它由__builtins__模块的名字构成,随后加载global namespace。 如果在执行期间调用了一个函数,那么将创建局部名字空间。

Python中一切都是object,包括function、module、class、package,这些objects都有在内存中真真正正的存在。每个object都有自己的namespace,每个object的namespace是独立的,可以通过object.name的方式访问object的namespace中的name,因此,不同的namespace中可以使用相同的name,而不会引发混淆。namespace是动态创建的,每一个namespace的生存时间也不一样。例如,一个module的namespace是它被import的时候创建的。而function被调用时,创建其local namespace,调用结束或抛出exception的时候, 该local namespace将被删除。

 

locals( )和globals( )分别返回dictionary结构的global namespace和local namespace,例如:

a = 3

def proc():
    a = 3
    print(locals())
    print(globals())
    
proc()

可以看见,局部作用域和全局作用域都有变量a,但它们不是同一个对象。

 

注意:还有一个特殊的module,一进入python解释器,就建立了一个module,这个module的namespace就是global namespace,一个全局唯一的namespace。这个module的一个内部的attribute,__name__等于__main__。如果模块是被导入的,__name__的值为模块的名字;如果模块是被直接执行的,__name__的值为’__main__’。

scope(作用域):用unqualified reference name(即与object.name相比,没有object的前缀)就可以直接找到name所指的对象。 LGB规则用scope的概念来解释就是:在任何代码执行的时候,都至少有3个scope,从内到外一次查找一个unqualified reference name。

 

 


 

 

函数体内的局部变量和全局变量如果重名,全局变量不可见(被局部变量覆盖)。

x = 50

def func(x):
    print('x=', x)  #50
    x = 2           
    print('x=', x)  #2

func(x)

print('x=', x)      #50

 

一个更复杂的例子:

j, k = 1, 2
 
def proc1():
    j, k = 3, 4
    print "j==%d and k=%d" %(j,k)
    k = 5

def proc2():
    j = 6
    proc1()
    print "j==%d and k=%d" %(j,k)

k = 7
proc1()
print "j==%d and k=%d" %(j,k)

j = 8
proc2()
print "j==%d and k=%d" %(j,k)

 

 

 

 

当在函数中需要修改全局变量时,如果没有global关键字则会出错:

x = 50

def run():
    print x
    x = 2  

run()

报错为:UnboundLocalError: local variable 'x' referenced before assignment

 

加上global关键字以后则OK

x = 50

def run():
    global x
    x = 2  

run()
print x     #2

 

posted @ 2015-01-06 16:58  如果的事  阅读(1911)  评论(0编辑  收藏  举报