2012-09-04 54 views
13

Python的C API函數PyEval_EvalCode讓我們在執行編譯Python代碼中運行Python代碼。我想執行一段Python代碼,就好像它在函數的範圍內執行一樣,以便它具有自己的不影響全局狀態的局部變量字典。C Python而:上下文

這似乎很容易做到,因爲PyEval_EvalCode讓你提供一個全局和局部字典:

PyObject* PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)

我碰上了與Python如何查找變量名做的問題。考慮下面的代碼,我跟PyEval_EvalCode執行:

myvar = 300 
def func(): 
    return myvar 

func() 

這個簡單的代碼實際上提出了一個錯誤,因爲Python是無法找到變量myvarfunc內。即使myvar位於外部作用域的本地字典中,Python也不會將其複製到內部作用域的本地字典中。原因如下:

每當Python查找變量名時,首先檢查locals,然後檢查globals,最後檢查builtins。在模塊範圍localsglobals是相同的字典對象。因此,在模塊範圍內聲明x = 5將放置x在該locals字典,這也是globals字典。現在,在一個需要查找模塊範圍內定義的函數x將找不到函數範圍內localsx,因爲Python不模塊範圍當地人複製到功能範圍的當地人。但這通常不是問題,因爲它可以找到xglobals

x = 5 
def foo(): 
    print(x) # This works because 'x' in globals() == True 

這是唯一與嵌套功能是Python似乎外範圍當地人複製到內範圍本地人。 (這似乎也是這樣做的懶惰,只有當它們在內部範圍內需要。)

def foo(): 
    x = 5 
    def bar(): 
     print(x) # Now 'x' in locals() == True 
    bar() 


所以這一切的結果是,在模塊範圍執行代碼時,你必須請確保您的全局字典和本地字典是SAME對象,否則,模塊範圍函數將無法訪問模塊範圍變量。

但在我的情況,我不想讓全球詞典和本地詞典是相同的。所以我需要一些方法告訴Python解釋器,我正在函數範圍內執行代碼。有沒有辦法做到這一點?我查看了PyCompileFlags以及PyEval_EvalCodeEx的其他參數,並且找不到任何方法來執行此操作。

回答

3

Python不實際複製外範圍當地人到內範圍當地人; locals的文檔狀態爲:

當在函數塊中調用自由變量時,locals()會返回自由變量,但不在類塊中調用它們。

這裏「自由」變量指的是由嵌套函數關閉的變量。這是一個重要的區別。

您的具體情況,最簡單的解決方法是僅僅通過相同字典對象爲globalslocals

code = """ 
myvar = 300 
def func(): 
    return myvar 

func() 
""" 
d = {} 
eval(compile(code, "<str>", "exec"), d, d) 

否則,你可以用你的代碼的函數,從編譯對象中提取:

s = 'def outer():\n ' + '\n '.join(code.strip().split('\n')) 
exec(compile(s, '<str>', 'exec').co_consts[0], {}, {}) 
+0

@ Channel72見上文。 – ecatmur