2012-12-10 142 views
1

有沒有在Python腳本中創建Python代碼然後執行/測試它的方法?有沒有辦法編寫一個創建和執行代碼的Python腳本?

我的功能有以下類型形式的(作爲一個例子)

def f(n): 
    if n<=3: return [0, 0, 6, 12][n] 
    return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4) 

但我希望能夠創建這些類型的動態功能(或與此有關的任意功能),然後測試他們的在運行時輸出(而不是將此函數複製/粘貼到腳本中,然後手動測試它)。

不確定這是否合理,如有需要請諮詢詳細說明。我已經看過eval和exec,但無法讓它們與整個函數定義一起工作,只是像1 + 2等基本語句。

+2

你要完成的任務叫做_metaprogramming._ –

+0

你想在哪一點創建這些功能?從運行期間的「用戶輸入」?或者像你在提供一個插件模塊一樣動態,在啓動時會加載一堆函數? – jdi

+0

你需要更具體。你是如何創建這些功能的?是否必須以程序文本的形式,而不是以本地定義的函數(或lambda)或驅動不同函數的數據爲形式?如果您真的需要即時評估文本,有辦法做到這一點(甚至可以使用手動編譯的字節碼來創建函數......),但是如果可能的話,應該避免這樣做,因爲它通常是錯誤的答案在Python中。 – abarnert

回答

4

有很多方法可以做這種事情。

如果該功能可以在沒有「跨越語言」的情況下進行描述,那麼您可以定義一個本地函數並返回它,就像在Blender的答案中一樣。這通常是你想要什麼,當你認爲你需要定義新的功能(借用Blender的例子):

def make_func(a, b): 
    def f(n): 
     return n**a + b 
    return f 

有時候,你可以做的更好,並代表功能的數據。例如,你如何創建一個任意的多項式函數?那麼,你不需要;你可以有一個通用的多項式函數,該函數接受一系列的係數和一個值並對其進行評估;那麼你需要做的就是創建係數列表。

事實上,我認爲這是你想要的。如您所說:

它可以返回2 * f(n-1)-4 * f(n-2)-5 * f(n-3)+ 15 * f(n-4) (n-1)+ 3 * f(n-2),或f(n-1)+ f(n-2)+ f(n-3)+ f(n-4)+ 5 * f(n-5)取決於我需要它。

這絕對可以被表示爲係數的名單:

def make_recursive_func(coefficients, baseval): 
    def f(n): 
     if n < len(coefficients): return baseval[n] 
     return sum(coefficient * f(n-i-1) for i, coefficient in enumerate(coefficients)) 
    return f 

但它可能是更簡單寫一個eval_recursive_func(coefficients, baseval),如果你曾經要與返回的函數來完成所有的呼叫它立即,然後忘記它。

有時 - 很少,但不是永遠 - 您確實需要執行代碼。正如Himanshu所說,evalexec和朋友是這樣做的。例如:現在

newcode = ''' 
def f(n): 
    if n<=3: return [0, 0, 6, 12][n] 
    return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4) 
''' 
exec(newcode) 

f功能已經被定義,完全一樣,如果你只是做到了這一點:

def f(n): 
    if n<=3: return [0, 0, 6, 12][n] 
    return 2*f(n-1) - 4*f(n-2) - 5*f(n-3) + 15*f(n-4) 

它在PY 3比的Py2有點不同,有根據的變化你希望執行什麼樣的上下文,或者你是否希望它執行或評估或編譯或像導入一樣對待等。但這是基本思想。

如果你想不出爲什麼要寫第一個而不是第二個,那麼你不需要這個。

如果你無法弄清楚如何快速生成正確的字符串,你不應該這樣做。正如Ignacio Vazquez-Abrams所指出的,如果這些函數可以由用戶輸入構建出來,則需要通過迭代編譯和行走AST來做一些事情來驗證它們是否安全。

最後,更是很少,你需要使用new模塊(和/或inspect)創建一個新的函數對象上飛出來的其他功能對象(甚至從手工製作的字節碼)位。但是如果你需要知道如何做到這一點,你可能已經知道如何做。

+0

哇,這個作品!聖潔的母親,這工作完美無瑕。它是否需要完全相同的間距/格式/等?如果我正在創建這個動畫,我是否需要製表符和用於換行符的\ r \ n? – KaliMa

+0

那麼,你真的不應該在Python代碼中有選項卡。你應該有'\ n'而不是'\ r \ n'。但是無論你使用什麼,你的字符串都應該和你在腳本中輸入的內容完全一樣,或者輸入到解釋器中。當然,您可以使用'\ t'和'\ n'轉義符代替'''''字符串。同時,我不認爲這是你真正想要做的......特別是如果你不想了解'compile','ast'等模塊......但是如果我錯了,這就是你做到這一點。 – abarnert

+0

我認爲你的「make_recursive_func」方法可能會更好。我怎麼實際測試f(n)呢?我嘗試輸出make_recursive_func(係數,baseval)(5)來打印f(5),例如 – KaliMa

3

如果您的函數類似,可以使用另一個函數創建它們:

def make_func(a, b): 
    def f(n): 
     return n**a + b 

    return f 

使用make_func轉變這個函數的定義:

def g(n): 
    return n**2 + 1 

逼到這樣的:

g = make_func(2, 1) 

在你的情況,這樣的事情應該工作:

def create_f(start_condition, vars, coeff_pairs): 
    def x(n): 
     if n <= start_condition: 
      return vars[n] 

     result = 0.0 

     for coeff, shift in coeff_pairs: 
      result += coeff * x(n + shift) 

     return result 

    return x 

而且你可以把它叫做:

f = create_f(3, [0, 0, 6, 12], [(2, -1), (-4, -2), (-5, -3), (15, -4)]) 

輸出的硬編碼函數的輸出相匹配。

+0

正確,但有時可能有不同的起始條件,條件數,遞歸子組件數等。 – KaliMa

+0

@KaliMa:你可以給一些例子? – Blender

+0

可以返回2 * f(n-1)-4 * f(n-2)-5 * f(n-3)+ 15 * f(n-4)1分鐘,或返回f(n-1 )+ 3 * f(n-2)或f(n-1)+ f(n-2)+ f(n-3)+ f(n-4)+ 5 * f(n-5)我需要它。初始條件同樣適用。可能是n <= 5:返回[1,2,3,4,5,6] [n]等 - 任何東西。 – KaliMa

2

它非常有意義。爲了這個目的,Python甚至有一個明確的set of modules。確保你在執行該功能之前先行走AST並驗證節點,以確保某人沒有偷偷溜進那裏的os.system('rm -rf /')

+0

我實際上是通過谷歌瀏覽過這個頁面,但找不到任何能夠讓我綜合實際功能的東西 - 只是表達式如3 + 4等。 – KaliMa

+0

@KaliMa:假設你在Python 2中,'eval'函數只能評估表達式,而不是語句(或套件),並且不能在表達式中定義函數(除了lambda-這不起作用,因爲除此之外,您需要遞歸)。 – abarnert

相關問題