博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python基础:名称空间与作用域
阅读量:6815 次
发布时间:2019-06-26

本文共 3330 字,大约阅读时间需要 11 分钟。

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 = 2def myFunc():	globalVar = globalVar + 2	print globalVar

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

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

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

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

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

globalVar = 2def 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': 
}

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

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

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

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

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

class MyBase(object):	passMyBase.attrA = 'foo'MyBase.attrB = 'bar'baseObj = MyBase()baseObj.attrA = 'hello'baseObj.attrC = 'world'print baseObj.attrA # print helloprint baseObj.attrB # print barprint 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):	passderivedObj = Derived()print derivedObj.varBase

 

转载于:https://www.cnblogs.com/stemon/p/5098236.html

你可能感兴趣的文章
zabbix3.x安装出现“configure: error: Not found mysqlclient library”的解决办法
查看>>
Windows Server 2008 R2和Windows 7脱机加入域
查看>>
让Windwos Server 2008 R2 SP1的FTP真正能访问
查看>>
SCOM-Agent安装—基于Windows工作组
查看>>
简单开发k8s多集群后台
查看>>
Linux MYSQL数据库系统部署及基本语句
查看>>
【01】KVM中小企业实践-概述
查看>>
SVN与Apache整合
查看>>
xml帮助提示
查看>>
assoc 和 ftype
查看>>
搭建mysql主从服务器
查看>>
linux下查看和修改文件时间
查看>>
我的友情链接
查看>>
linux资源分配Cgroup用法
查看>>
圆包含最多点问题
查看>>
Windows 8.1 发布了一个称为“Defender”的新模块
查看>>
浅析apache调优
查看>>
我的友情链接
查看>>
【Linux】如何正确安装Tomcat
查看>>
010-电脑软件安装手册-20190418
查看>>