Python 在區域性變數域中執行程式碼
問題
你想在使用範圍內執行某個程式碼片段,並且希望在執行後所有的結果都不可見。
解決方案
為了理解這個問題,先試試一個簡單場景。首先,在全域性名稱空間內執行一個程式碼片段:
>>> a = 13 >>> exec('b = a + 1') >>> print(b) 14 >>>
然後,再在一個函式中執行同樣的程式碼:
>>> def test(): ... a = 13 ... exec('b = a + 1') ... print(b) ... >>> test() Traceback (most recent call last): File "<stdin>",line 1,in <module> File "<stdin>",line 4,in test NameError: global name 'b' is not defined >>>
可以看出,最後丟擲了一個NameError異常,就跟在 exec()
語句從沒執行過一樣。 要是你想在後面的計算中使用到 exec()
執行結果的話就會有問題了。
為了修正這樣的錯誤,你需要在呼叫 exec()
之前使用 locals()
函式來得到一個區域性變數字典。 之後你就能從區域性字典中獲取修改過後的變數值了。例如:
>>> def test(): ... a = 13 ... loc = locals() ... exec('b = a + 1') ... b = loc['b'] ... print(b) ... >>> test() 14 >>>
討論
實際上對於 exec()
的正確使用是比較難的。大多數情況下當你要考慮使用 exec()
的時候, 還有另外更好的解決方案(比如裝飾器、閉包、元類等等)。
然而,如果你仍然要使用 exec() ,本節列出了一些如何正確使用它的方法。 預設情況下,exec() 會在呼叫者區域性和全域性範圍內執行程式碼。然而,在函式裡面, 傳遞給 exec() 的區域性範圍是拷貝實際區域性變數組成的一個字典。 因此,如果 exec() 如果執行了修改操作,這種修改後的結果對實際區域性變數值是沒有影響的。 下面是另外一個演示它的例子:
>>> def test1(): ... x = 0 ... exec('x += 1') ... print(x) ... >>> test1() 0 >>>
上面程式碼裡,當你呼叫 locals()
獲取區域性變數時,你獲得的是傳遞給 exec()
的區域性變數的一個拷貝。 通過在程式碼執行後審查這個字典的值,那就能獲取修改後的值了。下面是一個演示例子:
>>> def test2(): ... x = 0 ... loc = locals() ... print('before:',loc) ... exec('x += 1') ... print('after:',loc) ... print('x =',x) ... >>> test2() before: {'x': 0} after: {'loc': {...},'x': 1} x = 0 >>>
仔細觀察最後一步的輸出,除非你將 loc 中被修改後的值手動賦值給x,否則x變數值是不會變的。
在使用 locals()
的時候,你需要注意操作順序。每次它被呼叫的時候, locals()
會獲取區域性變數值中的值並覆蓋字典中相應的變數。 請注意觀察下下面這個試驗的輸出結果:
>>> def test3(): ... x = 0 ... loc = locals() ... print(loc) ... exec('x += 1') ... print(loc) ... locals() ... print(loc) ... >>> test3() {'x': 0} {'loc': {...},'x': 1} {'loc': {...},'x': 0} >>>
>>> def test3(): ... x = 0 ... loc = locals() ... print(loc) ... exec('x += 1') ... print(loc) ... locals() ... print(loc) ... >>> test3() {'x': 0} {'loc': {...},'x': 0} >>>
注意最後一次呼叫 locals()
的時候x的值是如何被覆蓋掉的。
作為 locals()
的一個替代方案,你可以使用你自己的字典,並將它傳遞給 exec()
。例如:
>>> def test4(): ... a = 13 ... loc = { 'a' : a } ... glb = { } ... exec('b = a + 1',glb,loc) ... b = loc['b'] ... print(b) ... >>> test4() 14 >>>
大部分情況下,這種方式是使用 exec()
的最佳實踐。 你只需要保證全域性和區域性字典在後面程式碼訪問時已經被初始化。
還有一點,在使用 exec()
之前,你可能需要問下自己是否有其他更好的替代方案。 大多數情況下當你要考慮使用 exec()
的時候, 還有另外更好的解決方案,比如裝飾器、閉包、元類,或其他一些超程式設計特性。
以上就是Python 在區域性變數域中執行程式碼的詳細內容,更多關於Python 區域性變數域的資料請關注我們其它相關文章!