名字 symbolic name
An assignment statement creates a symbolic name that you can use to reference an object. The statement
x = 'foo'
creates a symbolic namex
that refers to the string object'foo'
.
命名空间 Namespaces
A namespace is a collection of currently defined symbolic names along with information about the object that each name references. You can think of a namespace as a dictionary in which the keys are the object names and the values are the objects themselves. Each key-value pair maps a name to its corresponding object.
命名空间是一组名字及其所指对象的容器,可理解成字典,名字为键(key),对象为值(value)
在一个命名空间中,名字是唯一的,而不同命名空间中可以有相同名字
不同的命名空间把相同名字的不同对象隔开,解决了名字冲突的问题
变量作用域 Variable Scope
The existence of multiple, distinct namespaces means several different instances of a particular name can exist simultaneously while a Python program runs. As long as each instance is in a different namespace, they’re all maintained separately and won’t interfere with one another.
命名空间解决了名字冲突的问题,而变量作用域则规定了查找名字的规则LEGB(从哪个命名空间中查找名字),划分了访问名字的权限(外部作用域无法访问内部作用域的名字)
我感觉命名空间和变量作用域在很多时候是一个意思,那到底有什么区别呢?
只有模块(module),类(class),函数(def, lambda)会产生新的命名空间,其他如流程控制的代码块(if, for, try等)不会产生新命名空间。产生新命名空间意味着外部无法访问新命名空间内定义的变量
从外到内分为4种命名空间:
- Built-in(内建):python自带的变量
- Global or module(全局)
- Enclosing or nonlocal(闭包):既不是全局也不是局部,通常出现在闭包语境下
- Local or function(局部)
查找一个名字是否可用的顺序是:L->E->G->B(LEGB rule),区域由内到外
global & nonlocal
改变默认的LEGB规则:在内部空间使用,指定访问哪个命名空间的名字,同时赋予修改权限(允许修改外部变量)
内部作用域虽然可以自由访问外部作用域的变量值,却无法直接修改
想在内部作用域修改外部作用域的变量时,就需要用global
和nonlocal
关键字声明一下:
gv = 1 # 全局变量--不可变数据类型
gvs = [1] # 全局变量--可变数据类型
def outer():
global gv
gv += 2
gvs[0] += 2
global lazy_gv
lazy_gv = 10
nlv = 11 # 非局部变量--不可变数据类型
nlvs = [11] # 非局部变量--可变数据类型
def inner():
nonlocal nlv
nlv += 22
nlvs[0] += 22
print(nlv, nlvs) # 11 [11]
inner()
print(nlv, nlvs) # 33 [33]
print(gv, gvs, ('lazy_gv' in globals())) # 1 [1] False
outer()
print(gv, gvs, ('lazy_gv' in globals())) # 3 [3] True
对于可变数据类型,由于没有修改容器本身(容器本身的 id
没变),所以不声明也能修改容器内的值
获取指定作用域内的变量:
globals()
:获取所有全局变量,还可以修改变量值(字典)
locals()
:在函数内调用时,获取函数内所有局部变量(字典)
vars([object])
:返回object
的__dict__
属性(字典),不传参数返回locals()
dir([object])
:返回object
命名空间中的名字(列表),"__dict__"
是其中之一;不传参数返回当前命名空间中的名字(列表)
__dict__
:存放对象的可写属性,可以通过该字典修改对象属性
总结
内部命名空间可以访问外部命名空间的名字,但不能修改,要修改需要加global
或nonlocal
外部空间不能访问内部变量
Python Scope & the LEGB Rule: Resolving Names in Your Code – Real Python