为何eval('bar')触发NameError,添加print(bar)后却无报错?
嵌套作用域中eval访问变量的问题解析
问题复现
触发NameError的代码
def foo(): def bar(): return 'blah' def baz(): eval('bar') baz() foo()
完整报错信息
Traceback (most recent call last): File "main.py", line 7, in <module> foo() File "main.py", line 6, in foo baz() File "main.py", line 5, in baz eval('bar') File "<string>", line 1, in <module> NameError: name 'bar' is not defined
无错误的代码
def foo(): def bar(): return 'blah' def baz(): print(bar); eval('bar') baz() foo()
这段代码会打印类似 <function foo.<locals>.bar at 0x7fd5fdb24af0> 的内容,且不会触发任何报错。
问题解答
第一段代码为何无法访问bar?
这是Python静态作用域解析机制和eval特性共同作用的结果:
- Python编译函数
baz时,会扫描函数内的变量引用。第一段代码里,baz内部没有直接写bar的引用,只有eval的字符串参数里提到了bar——编译阶段无法解析字符串内容,所以Python不会把bar标记为需要从外层foo作用域查找的变量。 - 当
eval执行时,默认使用当前baz的局部作用域查找变量,但baz自身没定义bar,也没有关联外层的bar,因此抛出NameError。
提前执行print(bar)为何能解决问题?
当baz里加入print(bar)后,Python编译baz时会直接扫描到对bar的显式引用,此时会把bar标记为自由变量,即需要从外层嵌套的foo作用域中查找。
- 后续
eval执行时,baz的作用域已经关联了外层的bar变量,自然就能找到它。
本质原因
这不是eval的bug,而是Python的静态作用域规则决定的:变量的查找路径在编译阶段就确定了,eval的字符串参数属于动态内容,编译阶段无法识别其中的变量引用,也就不会为其建立外层作用域的查找关联。只有显式的变量引用,才会让Python在编译时标记为自由变量,让后续动态执行(比如eval)能找到对应变量。
内容的提问来源于stack exchange,提问作者joseville
相关产品推荐
相关产品推荐

