下面有 6 个例子,都挺相似,但结果比较费解。求一个能够完整解释 6 个例子的分析。
# 例 0
def foo():
exec('y = 1 + 1')
z = locals()['y']
print(z)
foo()
# 输出:2
# 例 1
def foo():
exec('y = 1 + 1')
y = locals()['y']
print(y)
foo()
# 报错:KeyError: 'y'
# 例 2
def foo():
y = 1 + 1
y = locals()['y']
print(y)
foo()
# 2
# 例 3
def foo():
exec('y = 1 + 1')
boc = locals()
y = boc['y']
print(y)
foo()
# KeyError: 'y'
# 例 4
def foo():
boc = locals()
exec('y = 1 + 1')
y = boc['y']
print(y)
foo()
# 2
# 例 5
def foo():
boc = locals()
exec('y = 1 + 1')
print(locals())
y = boc['y']
print(y)
foo()
# KeyError: 'y'
{'boc': {...}}
1
so1n 2019-05-14 13:01:41 +08:00 via Android
作用域问题,定义与声明混乱了,查看字节码能看得出区别
|
2
ec0 2019-05-14 13:42:47 +08:00
事实 1:locals() dict 是局部变量空间的拷贝, 不等于 局部变量空间
猜想 1:exec('y = 1 + 1') 相当于 locals()['y'] = 2 猜想 2 : 调用 locals() 会刷新对应的 dict 对象(与局部变量空间同步),把定义并赋值的局部变量加入 locals() dict,把定义但未赋值的局部变量从 locals() dict 中清除 例 5: print(locals()) 第二次调用了 locals() ,根据猜想 2,locals() dict 和局部变量空间同步,而局部变量空间里,y 已经定义但是没有赋值,locals() dict 会清除 y,所以 KeyError 例 4: boc = locals() 调用 locals() ,locals()['y'] = 2 后,没有再次调用 locals() ,locals()['y'] 得以保留 例 3: locals()['y'] = 2 之后调用了 locals() ,locals () dict 刷新,KeyError 例 2: y = 1+1,y 已定义并赋值,locals() dict 有 key y,输出 2 例 1: locals()['y'] = 2 后,y = locals()['y'] 调用了 locals() ,刷新 locals() dict,KeyError 例 0: locals()['y'] = 2 后执行 z = locals()['y'] ,调用了 locals() ,刷新 locals() dict,刷新以后仍然可以输出 2,所以有了猜想 3 猜想 3:在刷新 locals() dict 时,如果一个 key 不是局部变量,那么刷新时得以保留 参考: [为什么 Python 里面的 locals()是只读的]( https://zhuanlan.zhihu.com/p/21815224) |
3
chinesehuazhou OP @ec0 谢谢解答,不过我还是觉得不够全面,详细地写了一篇文章,欢迎交流: https://neue.v2ex.com/t/565190
|
4
chinesehuazhou OP @ec0 主要是例 5 的 print 结果,循环引用不应该忽视
|