2013-05-14 68 views
3

我遇到了幾個相關的答案,但不是我想要的。來自字符串的Python動態函數

這裏是我現在的代碼:

code_str = """ 
print "this is my global x = " + x 
print "And another line is done" 
""" 

x = 'mystery' 

func_template = lambda p: None 

func_template.__code__ = compile(code_str, '<string>', 'exec') 
func_template() # this executes fine and also has access to x, which i do need. 
# func_template('param') # This raises: TypeError: <module>() takes no arguments (1 given) 

一些背景知識;該code_str會從數據庫中來了,我需要存儲大量的功能在一個字典,所以我可以調用名稱的任何一個,象下面這樣:

all_funcs = {} 

# Assuming db_result returns a list of name, code tuples from the databse 

for name, code in db_result: 
    all_funcs[name] = my_compile(code) 

我想接着剛纔打電話與所需的功能論據我想,如果我知道這個名字:

result = all_funcs[by_name](arg1, arg2) 

編輯:數據庫是可信的,所以我並不需要santize或擔心惡意代碼。

回答

1

如果替換lambda表達式的__code__對象,則基本上重新定義該函數。新的argcount由__code__.co_argcount確定,所以lambda採用哪一個或多少個參數並不重要。

如果你想一個參數傳遞到您的編譯代碼,你可以嘗試eval代碼對象直接,傳遞你的參數在locals dictionaray:

code_str = """ 
print "this is my global x = " + x 
print "And another line is done" 
print param 
""" 

compiled = compile(code_str, "<string>", "exec") 
func_template = lambda p=None: eval(compiled, globals(), {'param': p}) 

x = "1" 
func_template() 
func_template("2") 

這樣,你顯然只能通過關鍵字參數,不可能使用位置參數。您還可以使用

func_template = lambda **kwargs: eval(compiled, globals(), **kwargs) 

將傳遞給函數的關鍵字參數直接傳遞給函數。

如果您需要函數的返回值,那麼您需要編譯'eval'模式的代碼,這意味着您必須將代碼限制爲表達式並且不能有語句。