2017-04-11 47 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