2017-04-11 22 views
3

语句eval'd似乎并未在具有相应全局变量和局部变量对象的环境中执行。eval全局变量和本地变量不能按预期工作

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也无法查找。

+0

这不是该问题的重复,因为NameError也出现在全局变量中。它不是一个全局变量与当地人的问题 – Scott

回答

2

当你定义f,它决定对g的引用是全局名称查找。此外,它保留了对定义时有效的全球环境的引用(这就是让你可以在其他模块中调用函数而不会剥夺它们的原始全局变量)的参考。

当您删除g时,您基本上破坏了f - 此全局查找g现在将失败。

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

+0

在我提供的环境中是否有某种方法可以评估(f)? – Scott

+1

那么,你可以'执行'函数的主体(而不是函数的调用),或者使用'types'模块构造一个新的函数对象,其中使用与原始函数不同的'func_globals'。或者你可以完全避免这种复杂的混乱:如果一个函数的操作需要定制,那么*将这些细节作为参数传递*。 – jasonharper

+0

好吧,为了说明这一点,我正在组建一个允许远程执行f的分布式计算系统。即我在jupyter笔记本上创建了一堆代码,发现f需要花费数周,将f和所有需求发送给大型服务器,大型服务器嘎然而止,并将结果交回给用户。我的想法是,我可以将所有东西全部放入全局变量()中,然后执行。这显然是不正确的。 = c) – Scott

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 
+0

>>> exec(“print(globals()['g'])”,{** globals(),** funcs}) 似乎暗示事实上,全球有一个名为'g'的环境应该可以访问吗?如果eval/exec函数不使用它来查找对象,那么能够传入全局对象的意义何在?确实,它在哪里寻找g,如果不是全局变量()? – Scott

+0

@Scott:当'eval(“f(2)”,globals(),funcs)'执行时,有一个'g()'函数,但它在字典中作为_'locals'_参数传递给' eval()',所以它的字节码开头处的'LOAD_GLOBAL'失败。 – martineau

+0

>>> eval(“f(2)”,{** globals(),** funcs})?那也找不到g。 – Scott