Python函数g()局部命名空间含f_var的原因及移除print后的疑问
locals()的疑问 我从Python文档了解到,函数的局部命名空间在函数被调用时创建。但在以下测试代码中,调用locals()获取函数g()的局部命名空间(字典类型)时,结果里居然包含了标识符f_var——这个变量在g()函数体内是出现在locals()调用之后的代码中的。更奇怪的是,移除print(f_var)这一行后,locals()返回的字典里就不再有f_var了。
测试代码
# local_namespace_test.py def f(): f_var = 55 def g(): g_var = 3 print(f"locals in g(): {locals()}") print(f_var) return g() return f()
正常运行输出
$ python3 local_namespace_test.py locals in g(): {'g_var': 3, 'f_var': 55} 55
注释掉print(f_var)后的输出
$ python3 local_namespace_test.py locals in g(): {'g_var': 3}
疑问
- 为什么此场景下
g()的局部命名空间会包含f_var? - 为什么移除
print(f_var)后,locals()返回的字典中就不再有f_var?
解答
这背后是Python编译阶段的变量解析规则在起作用:
编译期确定变量集合:Python在编译函数
g()的时候,会扫描整个函数体的代码,找出所有需要引用的变量。当代码里有print(f_var)时,f_var会被识别为自由变量(即不在g()局部作用域定义、需要从外层作用域f()中获取的变量),这个信息会被记录在函数的闭包结构里。locals()的返回内容:locals()函数返回的字典,不仅包含当前函数的局部变量(比如g_var),还会包含函数编译时确定的所有自由变量。哪怕locals()的调用在print(f_var)之前,因为编译阶段已经确定g()需要引用f_var,所以调用g()时,这个自由变量会被纳入到locals()返回的字典中。移除引用后的变化:如果删掉
print(f_var)这行,g()的代码里不再引用f_var,编译阶段就不会把它标记为自由变量。此时g()的局部命名空间里只有自己定义的g_var,locals()自然也就不会返回f_var了。
简单来说,locals()的内容不是按代码执行顺序动态生成的,而是基于函数编译时确定的变量集合来构建的——只要函数体里存在对外部变量的引用,哪怕引用在locals()之后,这个变量也会出现在locals()的结果里。
内容的提问来源于stack exchange,提问作者gambarimas87

