在前面《Python 代碼動態執行初探》中講到過如何用exec和eval函數動態執行代碼,也描述瞭如何限制其命名空間,避免動態代碼造成環境“污染”。
不過,有的時候,我們的確想要讓動態代碼生成一些局部或全局的定義——比如一個變量名——讓原始代碼或者後面的動態代碼可以繼續使用。像下面這樣直接定義是無法生效的:
def f():
a = 1
exec("a = 3")
print(a)
此時打印的結果會是1,也就是説exec中對a的修改被丟棄了。不過可以採用locals參數來取回修改:
def foo():
ldict = {}
exec("a=3",globals(),ldict)
a = ldict['a']
print(a)
這裏的打印結果就是3了。
本文的示例代碼取自stack overflow的一篇問答
2021-11-17更新
注意:在pycharm裏,默認的source root就是項目根目錄,這時不要在項目內部,再設置source folder了,要不然就會出現同一個Module,可以從不同級別import的問題。
我就曾經犯了這個錯誤,把一個項目子文件夾設置成source folder:
導致自動生成的import,全都少一級目錄,比如像下面這樣:
from core.data.Action import Action
class Executor
而實際上寫全路徑也能成功import:
from simplerpa.core.data.Action import Action
class STATE
這個錯誤還很隱蔽,因為平時使用沒有任何問題,直到有一個類成員屬性承擔了本文上面提到的ldict功能,但裏面保存的變量,在Executor執行的時候都丟失了,才發現原來從不同路徑import的同一個類,不能共享類成員。