Python学习之路(44)——命名空间与作用域
命名空间NameSpace
我们知道想要使用模块中的函数,就需要导入模块(import),此时就需要考虑命名空间的问题,否则使用函数就会报错。
命名空间是从名字到对象的一个映射(a mapping from name to objects),按照字典来实现,键是变量名,值就是变量的值。
一些常见的命名空间包括,built-in中的集合(abs()函数等),一个模块中的全局变量等。
从某种意义上来说,一个对象(object)的所有属性(attribute)也构成了一个命名空间。
在程序执行期间,会有多个命名空间同时存在:
1、每个函数拥有自己的命名空间,叫做局部命名空间,记录了函数的变量,包含函数的参数和局部定义的变量等;
2、每个模块也有自己的命名空间,叫做全局命名空间,记录了模块的变量,包含函数、类、其他导入的模块、模块级的变量等;
3、然后就是内置命名空间,任何模块都可以访问,包含内置函数和异常等。
作用域scope
作用域是Python程序的一块文本区域(testual region),在该区域,对命名空间是可以直接访问的,而不需要通过属性来访问。
作用域就是定义程序应当如何搜索确切的“名字-对象”的命名空间的层级关系。
注意:
直接访问:对一个变量名的引用会在所有命名空间中查找该变量,而不是通过属性访问。(比如abs(-1),abs就属于直接访问)
属性访问:所有名字后加 “.” 的都认为是属性访问。(比如module.fuction,需要知道function的命名空间,属性属性访问)
二者联系
在Python中,作用域是由命名空间按照特定的层级结构组合起来的。
因此,作用域一定是命名空间,但命名空间不一定是作用域。
LEGB-rule
在一个Python程序运行时,至少有4个作用域是存在的,直接访问一个变量可能在这些命名空间中逐一搜索。
Local(innermost):包含局部变量,比如一个函数/方法内部。
Enclosing:包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,内层函数可能搜索外层函数的命名空间,但该命名空间对内层函数而言,既非局部也非全局。
Global(next-to-last):当前脚本的最外层,比如当前模块的全局变量。
Built-in(outtermost):Python的__builtin__模块,包含了内建的变量/关键字等。
当有一个变量在Local域中找不到时,Python会在上一层Enclosing域(不一定存在)中搜索,如果找不到,则再往上一层,搜索模块内的Global域,如果还找不到,最后会在Build-in域中搜索。最终没有搜索到的话,Python会抛出NameError异常。
有两个例子:
示例1
def outer():
a = 0
b = 1
def inner():
print(a)
print(b)
inner()
outer()
##########执行结果##########
C:\Python35\python3.exe D:/Project/Python/Pro_py3/test.py
0
1
示例2
def outer():
a = 0
b = 1
def inner():
print(a)
print(b)
b = 4
inner()
outer()
##########执行结果##########
C:\Python35\python3.exe D:/Project/Python/Pro_py3/test.py
0
Traceback (most recent call last):
File "D:/Project/Python/Pro_py3/test.py", line 13, in <module>
outer()
File "D:/Project/Python/Pro_py3/test.py", line 11, in outer
inner()
File "D:/Project/Python/Pro_py3/test.py", line 7, in inner
print(b)
UnboundLocalError: local variable 'b' referenced before assignment
可以思考一下,为什么第一个示例正常运行,第二个示例抛出异常。
这里有2条规则很重要:
1、赋值语句通常会隐式地创建一个local变量,即便该变量名已存在赋值语句发生的上一层作用域中;
2、如果没有global关键字声明变量,对一个变量的赋值总是认为该变量存在于最内层(innermost)的作用域中。
浙公网安备 33010602011771号