为什么eval无法找到在外部函数中定义的变量?

17

我知道使用eval()通常意味着不良代码,但我遇到了一些奇怪的eval()函数在内部函数中的行为,我无法理解。如果我们写:

def f(a):
    def g():
        print(eval('a'))
    return g()

在这种情况下运行f(1)会产生一个NameError,提示a未定义。不过,如果我们定义

def f(a):
    def g():
        b = a + 1
        print(eval('a'))
    return g()

运行 f(1) 将会打印出 1

在局部变量和全局变量之间发生了一些我无法完全理解的事情。当 a 用于某些操作时,它仅是 g() 中的一个局部变量吗?这里到底发生了什么?


1
请查看以下链接:https://dev59.com/plnUa4cB1Zd3GeqPdcLx - Gsk
2个回答

7
简而言之,由于eval是用于动态评估的,解释器无法知道它应该将a添加到g的局部作用域中。为了提高效率,解释器不会将不需要的变量添加到局部变量的dict中。
根据eval文档:
表达式参数将使用全局和本地命名空间字典作为全局和本地范围进行解析和评估(严格来说是条件列表)。
这意味着如果没有提供全局和本地作用域,则函数eval(expression)将使用globals()作为其默认全局作用域,locals()作为其默认本地作用域。
尽管在你的第一个示例中,a都没有被包含在其中。
def f(a):
    print("f's locals:", locals())
    def g():
        print("g's locals:", locals())
        print(eval('a'))
    return g()

f(1)

实际上,由于解释器在解析g的主体时看不到对a的引用,因此不会将其添加到其本地变量中。

要使其工作,您需要在g中指定nonlocal a

输出

f's locals: {'a': 1}
g's locals: {}
Traceback ...
...
NameError: name 'a' is not defined

在你的第二个示例中,ag 的本地变量中,因为它在该作用域中被使用。
def f(a):
    print("f's locals:", locals())
    def g():
        print("g's locals:", locals())
        b = a + 1
        print("g's locals after b = a + 1:", locals())
        print(eval('a'))
    return g()

f(1)

输出

f's locals: {'a': 1}
g's locals: {'a': 1}
g's locals after b = a + 1: {'a': 1, 'b': 2}
1

1

看起来eval()只能在本地(这里是g)或全局中查找变量,而不是其父环境(这里是f)。解决方法是将变量设置为全局变量。

def f(a):
    global b #注意,不能直接使用“global a”,否则会出现错误:“名称'a'是参数和全局的”
    b=a
    def g():
        print(eval('b'))
    return g()
f(1)

输出:1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接