python - 为什么 exec() 中的符号定义语句有时对本地符号表没有影响?
问题描述
以下代码片段按预期工作:
def test():
print(f'local symbol table before exec : {locals()}')
exec('a = 0')
print(f'local symbol table after exec : {locals()}')
test()
# printed result:
# local symbol table before exec : {}
# local symbol table after exec : {'a': 0}
但是,一旦我在函数a = 1
末尾添加符号定义语句test
,该语句似乎exec
对本地符号表没有影响:
def test():
print(f'local symbol table before exec : {locals()}')
exec('a = 0')
print(f'local symbol table after exec : {locals()}')
a = 1
test()
# printed result:
# local symbol table before exec : {}
# local symbol table after exec : {}
那么,为什么会这样呢?
这是我的猜测:在函数内部静态定义的符号将在编译时以某种方式保留,如果符号已经保留,则在 exec 函数内部动态调用的任何符号定义语句都无法修改本地符号表。
真的吗?在编译期间实际发生了什么?
额外测试 1:将 exec 参数替换为'a = 0\nprint(locals())'
def test():
print(f'local symbol table before exec : {locals()}')
exec('a = 0\nprint(locals())')
print(f'local symbol table after exec : {locals()}')
test()
# printed result:
# local symbol table before exec : {}
# {'a': 0}
# local symbol table after exec : {'a': 0}
def test():
print(f'local symbol table before exec : {locals()}')
exec('a = 0\nprint(locals())')
print(f'local symbol table after exec : {locals()}')
a = 1
test()
# printed result:
# local symbol table before exec : {}
# {'a': 0}
# local symbol table after exec : {}
如我们所见,符号a
在执行过程中成功添加到本地符号表中exec()
,但之后它就神奇地消失了a = 1
。
额外测试 2:在之前添加return
语句a = 1
def test():
print(f'local symbol table before exec : {locals()}')
exec('a = 0\nprint(locals())')
print(f'local symbol table after exec : {locals()}')
return
test()
# printed result:
# local symbol table before exec : {}
# {'a': 0}
# local symbol table after exec : {'a': 0}
def test():
print(f'local symbol table before exec : {locals()}')
exec('a = 0\nprint(locals())')
print(f'local symbol table after exec : {locals()}')
return
a = 1
test()
# printed result:
# local symbol table before exec : {}
# {'a': 0}
# local symbol table after exec : {}
在第二个函数a = 1
中是无法访问的test()
,但它仍然会影响exec()
.
即使是模块中的dis()
函数dis
也无法区分这两个test()
函数。输出完全相同,如下所示:
5 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('local symbol table before exec : ')
4 LOAD_GLOBAL 1 (locals)
6 CALL_FUNCTION 0
8 FORMAT_VALUE 0
10 BUILD_STRING 2
12 CALL_FUNCTION 1
14 POP_TOP
6 16 LOAD_GLOBAL 2 (exec)
18 LOAD_CONST 2 ('a = 0\nprint(locals())')
20 CALL_FUNCTION 1
22 POP_TOP
7 24 LOAD_GLOBAL 0 (print)
26 LOAD_CONST 3 ('local symbol table after exec : ')
28 LOAD_GLOBAL 1 (locals)
30 CALL_FUNCTION 0
32 FORMAT_VALUE 0
34 BUILD_STRING 2
36 CALL_FUNCTION 1
38 POP_TOP
8 40 LOAD_CONST 0 (None)
42 RETURN_VALUE
解决方案
根据文档:
注意:默认 locals 的行为如下面函数 locals() 所述:不应尝试修改默认 locals 字典。如果您需要在函数 exec() 返回后查看代码对局部变量的影响,请传递显式局部变量字典。
所以我相信这属于“意外行为”,但我想你可以去实现exec()
真正深入了解。
推荐阅读
- sql - 如何通过分组修复计数
- android - 为什么我在 android 9 pie 中得到空对象引用
- c# - 如何引发另一个 ListView 中存在的 ListView 的单击事件项
- mysql - 获取一个ID到最后一条记录的记录
- c++ - 如何在不复制数据的情况下在平面数组和多维数组之间进行转换?
- java - JAVA程序找到最大素数,但输出错误?
- apache-kafka - 每个 Kafka 主题有多种消息类型
- ios - 是否有 WWDC 2019 会议视频摘要?
- asp.net-core - 使用 nservicebus 访问处理函数外部的消息头
- json - 使用 gin gonic 返回文字 JSON 的最简单方法