码迷,mamicode.com
首页 > 编程语言 > 详细

python基础:名称空间与作用域

时间:2016-01-04 13:14:10      阅读:249      评论:0      收藏:0      [点我收藏+]

标签:

Python的变量定义后都有自己的作用域,每个作用域内都有名字空间。名称空间就是变量名称与对象的关联关系。Python中使用变量名引用对象,需要使用该变量时,就在命名空间中进行搜索,获取对应的对象。从目前python的实现上来讲,内部使用了字典,但是并不保证以后会更改实现,所以说现阶段,命名空间是一个字典(dictionary),它的键就是变量名,它的值就是那些变量的值。在一个Python程序运行中,至少有4个scopes是存在的。

直接访问一个变量可能在这四个namespace中逐一搜索。

  • Local(innermost)

    包含局部变量。
    比如一个函数/方法内部。

  • Enclosing

    包含了非局部(non-local)也非全局(non-global)的变量。
    比如两个嵌套函数,内层函数可能搜索外层函数的namespace,但该namespace对内层函数而言既非局部也非全局。

  • Global(next-to-last)

    当前脚本的最外层。
    比如当前模块的全局变量。

  • Built-in(outtermost)

    Python __builtin__ 模块。
    包含了内建的变量/关键字等。

那么,这么多的作用域,Python是按什么顺序搜索对应作用域的呢?

著名的”LEGB-rule”,即scope的搜索顺序:

Local -> Enclosing -> Global -> Built-in

每个函数都有着自已的名称空间,叫做局部名称空间

每个局部名称空间的外部的名称空间,叫做封闭区域;如内嵌函数的外部函数的局部名称空间,就是这个内嵌函数的封闭区域。

每个模块拥有它自已的名称空间,叫做全局名称空间

还有就是内置名称空间,任何模块均可访问它,它存放着内置的函数和异常。

 

当有一个变量在 local 域中找不到时,Python会找上一层的作用域,即 enclosing 域(该域不一定存在)。
enclosing 域还找不到的时候,再往上一层,搜索模块内的 global 域。最后,会在 built-in 域中搜索。
对于最终没有搜索到时,Python会抛出一个 NameError 异常。

嵌套函数的情况:

     1、先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
     2、然后是在父函数的命名空间中搜索
     3、接着是模块命名空间中搜索
     4、最后在内置命名空间中搜索

 

1. 全局作用域有全局名字空间,在程序开始运行时生成,在程序退出时消失,可以通过globals()函数或dir("__main__")来访问全局名字空间。

2. 函数定义的函数体内就是局部名字空间,在调用函数时生成,在调用结束就消失,在函数体内调用locals()可以访问局部名字空间。Python中有一个现象非常奇特,在函数体内对全局变量进行赋值时需要使用global关键字进行变量声明,这是因为Python中有一个局部赋值规则造成的。比如说:

globalVar = 2
def myFunc():
	globalVar = globalVar + 2
	print globalVar

上述代码就会抛出“UnboundLocalError: local variable ‘globalVar‘ referenced before assignment”的异常,要求变量必须先赋值。

如果在函数体内对变量进行赋值,python将名称添加到局部名字空间中右侧执行运算时,发现在局部名字空间中有globalVar,就尝试将globalVar加1,而局部名字空间中的globalVar尚未赋值,就会抛出异常

globalVar = 2
def myFunc():
	global globalVar
	globalVar = globalVar + 2
	print globalVar

将globalVar声明为全局变量就会解决上述问题。

3. Encolsing Function变量的名字空间。简单的说就是在函数体内定义的新函数的本地名字空间

globalVar = 2
def myFunc():
	funVar = 3
	def innerFunc():
		innerVar = 4
		innerVar2 = funVar
		print ‘inner locals:‘ + str(locals())
		#print ‘inner globals:‘ + str(globals())

	innerFunc()
	print ‘outer locals:‘ + str(locals())
	#print ‘outer globals:‘ +str(globals())

此处略去了global函数的输出:

inner locals:{‘funVar‘: 3, ‘innerVar2‘: 3, ‘innerVar‘: 4}
outer locals:{‘funVar‘: 3, ‘innerFunc‘: <function innerFunc at 0x0000000007BBC4A8>}

可以看出内部函数名外部函数本地名字空间的一部分,内部函数在查找名字时会先查找本地名字空间,然后是外部函数的本地名字空间(封闭区域enclosing),再就是全局变量,最后是在内置模块名字空间中查找。

4. 内置模块的名字空间。python在启动时,会自动创建__builtins__模块,在该模块中定义了python数据类型。遵循LEGB搜索规则,如果Python不能在本地名字空间中找到某个名称,就会在全局命名空间中继续寻找,最后内置模块中查找。可以通过__builtins__.__dict__获取内置模块的名字。

最后补充Python使用面向对象编程中属性查找方法:

首先要知道:从某种意义上来说,一个对象(object)的所有属性(attribute)也构成了一个namespace.

a. 关于类类型类实例对象的属性查找方法。比如以下的定义:

class MyBase(object):
	pass
MyBase.attrA = ‘foo‘
MyBase.attrB = ‘bar‘

baseObj = MyBase()
baseObj.attrA = ‘hello‘
baseObj.attrC = ‘world‘

print baseObj.attrA # print hello
print baseObj.attrB # print bar

print MyBase.attrC # AttributeError 

如果在实例对象中找不到相关的属性就在类类型的名字空间中查找,但是反过来,在类类型中找不到的属性,不会向关联的实例对象名字空间中进行查找。这种情况也比较容易理解,毕竟类类型与类实例是一对多的关系。

这里另外有个微妙的地方需要强调。实例对象的属性一旦定义就会覆盖类类型名字空间中定义的属性。看下面的例子:

class Base(object):
	var = ‘hello‘

obj1 = Base()
obj1.var = ‘world‘

obj2 = Base()
print ‘obj1 value = ‘ + str(obj1.var) + ‘| id = ‘ + str(id(obj1.var))
print ‘obj2 value = ‘ + str(obj2.var) + ‘| id = ‘ + str(id(obj2.var))
print ‘Base value = ‘ + str(Base.var) + ‘| id = ‘ + str(id(Base.var))

执行结果:

技术分享

b. 子类实例对象的属性如果在实例对象中查找不到,先在子类类型名字空间中查找,再向父类的名字空间中进行查找。

class PythonBase(object):
	varBase = ‘foo‘

class Derived(PythonBase):
	pass

derivedObj = Derived()
print derivedObj.varBase

 

python基础:名称空间与作用域

标签:

原文地址:http://www.cnblogs.com/stemon/p/5098236.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!