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. 全局作用域有全局名字空间,在程序开始运行时生成,在程序退出时消失,可以通过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基础:名称空间与作用域的更多相关文章
-
Python 的名称空间和作用域
最开始对名称空间的了解是在学习函数的时候,那时候知道了作用域的查找顺序,以及全局名称空间和局部名称空间,产生疑惑的时候为学递归的时候,那时候还没有名称空间这个概念,只知道递归有个最大深度,那时候以后递 ...
-
跟着太白老师学python day10 名称空间,作用域和取值顺序,变量的加载顺序
名称空间分为3种: 1. 全局名称空间 2. 内置名称空间 3. 局部名称空间(临时) 作用域 全局作用域 1全局名称空间 2 内置名称空间 局部作用域 ...
-
python函数名称空间与作用域、闭包
一.命名空间概念 1.命名空间(name space) 名称空间是存放名字的地方. 若变量x=1,1存放在内存中,命名空间是存放名字x.x与1绑定关系的地方. 2.名称空间加载顺序 python te ...
-
python函数----名称空间和作用域
一 名称空间 名称空间即存放名字与对象映射/绑定关系的地方. 对于x=3,Python会申请内存空间存放对象3,然后将名字x与3的绑定关系存放于名称空间中,del x表示清除该绑定关系. 在程序执行 ...
-
python基础知识13---函数对象、函数嵌套、名称空间与作用域、装饰器
阅读目录 一 函数对象 二 函数嵌套 三 名称空间与作用域 四 闭包函数 五 装饰器 六 练习题 一 函数对象 1 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 ...
-
python基础之函数对象,嵌套,名称空间和作用域
函数对象: 函数是第一类对象的含义是函数可以被当作数据处理 函数可用于: def func(): print(‘func’) 1.引用 f = func 把内存地址赋值给f 2.当作参数传给一个函 ...
-
Python 名称空间与作用域、闭包与装饰器
Python 的名称 Python 的名称(Name)是对象的一个标识(Identifier).我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的.说得有点玄乎,我们以例子说明. 例如 ...
-
python全栈开发-Day9 函数对象、函数嵌套、名称空间与作用域
一 .函数对象 一 .函数是第一类对象,即函数可以当作数据传递 可以被引用 可以当作参数传递 返回值可以是函数 可以当作容器类型的元素 二. 利用该特性,优雅的取代多分支的if def foo(): ...
-
python第十二天, 三元表达式, 函数对象,名称空间与作用域,函数的嵌套定义
复习 1. 字符串的比较: 2. 函数的参数:形参与实参 3. 实参的分类:位置实参与关键字实参 4. 形参分类: 1.无值位置形参 2.有值位置形参 3.可变长位置形参 4.有无值关键字形参 5.可 ...
-
python之函数对象、函数嵌套、名称空间与作用域、装饰器
一 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二 利用该特性,优雅的取代多分支的if de ...
随机推荐
-
normalize.css入门和下载
CSS Reset 是革命党,CSS Reset 里最激进那一派提倡不管你小子有用没用,通通给我脱了那身衣服,凭什么你 body 出生就穿一圈 margin,凭什么你姓 h 的比别人吃得胖,凭什么你 ...
-
C# ADO基础(使用using操作数据库)
1.使用using 来对数据库进行操作,using是资源释放的一种缩写,用于实现了实现了IDisposable接口(释放对象资源的接口是IDisposable) private void button ...
-
ExecutorService
接口 java.util.concurrent.ExecutorService 表述了异步执行的机制,并且可以让任务在后台执行.壹個 ExecutorService 实例因此特别像壹個线程池.事实上, ...
-
Matplotlib 使用 - 《Python 数据科学手册》学习笔记
一.引入 import matplotlib as mpl import matplotlib.pyplot as plt 二.配置 1.画图接口 Matplotlib 有两种画图接口: (1)一个是 ...
-
SQL mysql优化
慢查询 如何通过慢查日志发现有问题的SQL? 查询次数多且每次查询占用时间长的SQL pt-query-digest分析前几个查询 IO大的SQL pt-query-diges分析中的Rows exa ...
-
SharePoint 2013 APP 开发示例 系列
SharePoint 2013 APP 安全: SharePoint 2013 APP 开发示例 (一)List 读写 SharePoint 2013 APP 开发示例 (二)获取用户信息 Share ...
-
Spark(十二)--性能调优篇
一段程序只能完成功能是没有用的,只能能够稳定.高效率地运行才是生成环境所需要的. 本篇记录了Spark各个角度的调优技巧,以备不时之需. 一.配置参数的方式和观察性能的方式 额...从最基本的开始讲, ...
-
Fiddler大师之路系列(一)
江湖传言,Fiddler是捕获客户端与服务器之间的所有HTTP(S) 请求的利器,但是在具体使用过程中,发现使用Fiddler进行抓包时有一部分请求总是没到,多方苦寻之下发现客户端使用WinINET这 ...
-
ASPNET MVC Error 500.19
今天创建了一个新的ASPNET MVC 项目部署到本地, 生成成功后在浏览器中输入URL却发现报这个错 参照下面的文章我给IIS_IUSRS和IUSR(我比较懒直接everyone)赋予虚拟目录读写权 ...
-
Scrapy爬虫入门Request和Response(请求和响应)
开发环境:Python 3.6.0 版本 (当前最新)Scrapy 1.3.2 版本 (当前最新) 请求和响应 Scrapy的Request和Response对象用于爬网网站. 通常,Request对 ...