2016-08-01 88 views
0

我目前正在嘗試編寫一個Python(3.4.4)圖形用戶界面與tkinter應該允許適合一些任意功能的數據點。爲了簡單起見,我想創建一些輸入函數並對其進行評估。後來,我想用從scipy進行繪製和擬合。替代執行

爲了做到這一點,我想從用戶輸入字符串中創建一個動態(擬合)函數。我發現並閱讀了exec,但人們說(1)使用起來不安全,(2)總是有更好的選擇(例如here和許多其他地方)。所以,我想知道在這種情況下會有什麼替代方案?

下面是與工作兩個嵌套函數的一些示例代碼,但它不是動態:

def buttonfit_press(): 
    def f(x): 
     return x+1 
    return f 

print(buttonfit_press()(4)) 

這裏是一些代碼,引起NameError: name 'f' is not defined之前,我甚至可以開始使用XVAL:

def buttonfit_press2(xval): 
    actfitfunc = "f(x)=x+1" 
    execstr = "def {}:\n return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1]) 
    exec(execstr) 
    return f 

print(buttonfit_press2(4)) 

types.FunctionType另一種方法在這裏討論(10303248)未成功要麼...

小號o,我的問題是:是否有一個很好的選擇可以用於這種情況?或者,如果沒有,我如何使exec運行代碼?

我希望這是可以理解的,不要太模糊。預先感謝您的想法和意見。


@ GABOR鄂爾多斯:

要麼我不明白,或者我不同意。如果我在主循環同一段程序代碼,它承認f,我可以從execstr執行代碼段:

actfitfunc = "f(x)=x+1" 
execstr = "def {}:\n return {}\n".format(actfitfunc.split("=")[0], actfitfunc.split("=")[1]) 
exec(execstr) 
print(f(4)) 
>>> 5 

@盧卡斯Rogalski:

印刷execstr似乎沒什麼問題:

def f(x): 
    return x+1 

縮進錯誤不太可能是由於我的編輯器,但我仔細檢查過 - 沒關係。 介紹my_locals,稱其爲exec和印刷在事後表示:

{'f': <function f at 0x000000000348D8C8>} 

但是,我仍然得到NameError: name 'f' is not defined


@ user3691475:

你舉的例子非常相似,我的第一個例子。但是在我的理解中,這不是「動態的」,即在代碼運行時不能改變函數的輸出。


@Dunes:

我認爲這是朝着正確的方向,謝謝。但是,我不明白我如何評估和使用此功能在下一步?我的意思是:爲了能夠適應它,我必須提取擬合變量(即f(x)=a*x+b中的a)或評估不同x值的函數(即print(f(3.14)))。

+0

@GáborErdős技術上它是,因爲Exec將隱含引用傳遞'全局()'和'當地人()'和輸入參數,他們會突變。 –

+0

@nostradamus你的代碼應該可以正常工作。你的縮進是否正確?你可以運行:'my_locals = {}; exec(execstr,globals(),my_locals)'然後顯示存儲在'my_locals'中的內容?通過打印'execstr'進行完整性檢查也不會造成任何影響。 –

回答

0

我不知道究竟是你想幹什麼,即允許哪些功能,允許什麼操作等

這裏是一個函數發生器的一個動態參數的例子:

>>> def generator(n): 
     def f(x): 
      return x+n 
     return f 
>>> plus_one=generator(1) 
>>> print(plus_one(4)) 
5 
1

exec/eval的問題是它們可以執行任意代碼。因此,要使用execeval,您需要仔細解析代碼片段以確保它不包含惡意代碼(難以置信的艱鉅任務),或者確保代碼的來源可信。如果你正在製作一個個人使用的小程序,那很好。一個負責敏感數據或金錢的大計劃絕對不是。看起來你的用例被視爲具有可信來源。

如果您只想在運行時創建任意函數,那麼只需使用lambda表達式和eval的組合。例如。

func_str = "lambda x: x + 1" # equates to f(x)=x+1 
func = eval(func_str) 
assert func(4) == 5 

之所以你嘗試不工作是locals(),在功能的情況下,造成局部名字空間的副本。生成字典的變異不會影響當前的本地名稱空間。你需要做的是這樣的:

def g(): 
    src = """ 
def f(x): 
    return x + 1 
    """ 
    exec_namespace = {} # exec will place the function f in this dictionary 
    exec(src, exec_namespace) 
    return exec_namespace['f'] # retrieve f