eval globals和locals参数不能按预期工作

时间:2017-04-11 14:49:27

标签: python python-3.5

eval'd语句似乎并未实际在具有相应全局变量和局部对象的环境中执行。

def f(x):
    return g(x)*3
def g(x):
    return x**2
funcs = {"f":f,"g":g}
del globals()['g'] # to keep them out of the global environment
del globals()['f']
eval("f(2)",globals(),funcs)

错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "<stdin>", line 2, in f
NameError: name 'g' is not defined

更新

更多插图:

>>> exec("print(globals()['g'])",{**globals(),**funcs})
<function g at 0x7feb627aaf28>
>>> eval("f(2)",{**globals(),**funcs})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
  File "<stdin>", line 2, in f
NameError: name 'g' is not defined

修改

这不是this question的重复。即使作为全局函数传递,函数g也无法查找。

2 个答案:

答案 0 :(得分:3)

通过使用f()查看从函数dis.dis(f)定义编译的字节码可以看出问题:

  7           0 LOAD_GLOBAL              0 (g)
              2 LOAD_FAST                0 (x)
              4 CALL_FUNCTION            1
              6 LOAD_CONST               1 (3)
              8 BINARY_MULTIPLY
             10 RETURN_VALUE

如您所见,第一条指令尝试加载名为g全局

使其成功的一种方法是使g()成为本地函数:

def f(x):
    def g(x):
        return x**2

    return g(x)*3

funcs = {"f": f}
del globals()['f'] # to keep it  out of the global environment
print(eval("f(2)", globals(), funcs))  # -> 12

以下是修订版f()中的字节码进行比较:

  8           0 LOAD_CONST               1 (<code object g at 0x00546288, file "test.py">)
              2 LOAD_CONST               2 ('f.<locals>.g')
              4 MAKE_FUNCTION            0
              6 STORE_FAST               1 (g)

 11           8 LOAD_FAST                1 (g)
             10 LOAD_FAST                0 (x)
             12 CALL_FUNCTION            1
             14 LOAD_CONST               3 (3)
             16 BINARY_MULTIPLY
             18 RETURN_VALUE

答案 1 :(得分:2)

当您定义f时,它决定对g的引用是全局名称查找。此外,它在定义时保留了对有效全局环境的引用(这使得您可以在不剥夺其原始全局变量的情况下调用其他模块中的函数)。

当您删除g后,您从根本上打破了f - g的全局查询现在将失败。

exec调用的全局/本地环境参数对已编译的函数f没有任何影响。它们只会影响您执行的实际文字:"f(2)"。换句话说,实际使用您提供的环境的唯一名称查找是f本身的名称。