局部命名空间下
def foo():
a = a
if __name__ == '__main__':
foo()
报错信息:UnboundLocalError: local variable ‘a’ referenced before assignment
全局命名空间下
if __name__ == '__main__':
b = b
报错信息:NameError: name ‘b’ is not defined
为什么在不同的作用域下报错信息不一样呢?
要解决这个问题,就必须了解namespace,也就是命名空间。命名空间是变量名和对象本身的映射,就像是人名与人本身的映射一样。共有四种类型的命名空间:
-
局部命名空间(local):指的是一个函数或者一个类所定义的名称空间;包括函数的参数、局部变量、类的属性等。
-
闭包命名空间(enclosing function):闭包函数 的名称空间(Python 3 引入)。
-
全局命名空间(global):读入一个模块(也即一个.py文档)后产生的名称空间。
-
内建命名空间(builtin):Python 解释器启动时自动载入__built__模块后所形成的名称空间;诸如 str/list/dict…等内置对象的名称就处于这里。
命名空间是何时创建的呢?
-
内建命名空间在 Python 解释器启动时创建,之后会一直存在;
-
模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会保持到解释器退出。
-
函数调用时产生新的局部命名空间;函数返回结果、抛出异常时释放命名空间,每一次递归都生成一个命名空间。
-
标识符产生地点决定标识符所处的命名空间。
变量名是何时加入到命名空间的呢?
def foo():
# 创建了命名空间,但内容为空
print(locals())
if True:
# 将a加入到命名空间
a = 1
else:
# 因为没有运行,所以没有将b加入到命名空间
b = 2
print(locals())
if __name__ == '__main__':
foo()
# 运行结果
# {}
# {'a': 1}
从上面这个例子,可以知道,只有赋值语句被执行时,才会将相应的变量名加入到命名空间中。
那么对于a=a
,命名空间是如何处理的呢?
查了很多资料,也没有找到答案。但根据已知的内容,我 猜测 局部命名空间和全局命名空间对a=a
的处理方式并不同。
- 局部命名空间
遇到到a=a
时,python解释器发现了变量a,将其加入到命名空间。然后继续执行,从命名空间中找到了未赋值的a来给a赋值,就出现了UnboundLocalError: local variable ‘a’ referenced before assignment
错误。 - 全局命名空间
遇到到a=a
时,未将a加入到命名空间就执行,python解释器在命名空间找不到a,就出现了NameError: name ‘b’ is not defined
的错误。
二者的区别就在于a加入到命名空间是在赋值操作前还是在赋值操作后。