如果我正確理解你想要的東西,理論上是不可能的;您似乎要描述的轉換是對等效函數有不同影響的轉換,具體取決於其源代碼的表面細節,這些細節可能不會以編譯後的形式保留。例如,考慮給定函數的以下兩個版本:
def a (n, acc):
print('called a(%d,%d)' % (n, acc))
if n == 0: return acc
return a (n - 1, acc + n)
def a (n, acc):
print('called a(%d,%d)' % (n, acc))
if n == 0: return acc
ret = a (n - 1, acc + n)
return ret
顯然它們在功能上是相同的。在源代碼中,唯一的區別是前者直接在某個表達式上使用return
,而後者則將該表達式的結果保存到局部變量中,然後在該變量上使用return
。在編制的形式中,根本不需要任何差異。
現在考慮「打補丁」的版本:
def a (n, acc):
print('called a(%d,%d)' % (n, acc))
if n == 0: return acc
return lambda: a (n - 1, acc + n)
def a (n, acc):
print('called a(%d,%d)' % (n, acc))
if n == 0: return acc
ret = a (n - 1, acc + n)
return lambda: ret
顯然,這些都是非常不同:例如,如果n
是3
和acc
是0,那麼前者打印called a(3,0)
並返回打印called a(2,3)
功能和返回打印called a(1,5)
並返回一個函數,該函數打印called a(0,6)
並返回6
,而後者打印called a(3,0)
和called a(2,3)
和called a(1,5)
和called a(0,6)
並返回一個函數,該函數返回一個函數一個返回函數的函數,返回6
。
更廣泛的區別在於,每次調用新的返回值時,第一個「修補」函數執行一個計算步驟,而第二個「修補」版本執行初始調用期間所有計算步驟,以及只是爲了娛樂而安排了一系列隨後的電話。只要存在副作用(例如打印消息,或者如遞歸太深以至於溢出堆棧),這種差異就很重要。如果調用者引入了副作用也可能很重要:請注意,這些函數只會遞歸,直到其他一些代碼重新定義a
,此時計劃繼續重新調用的版本之間存在差異a
以及已完成所有呼叫的版本。
由於您無法區分兩個「未打補丁」版本,因此顯然無法生成您的轉換意味的獨特「已打補丁」版本。
您可以很好地區分兩個未打補丁的版本。第一個以「CALL_FUNCTION RETURN_VALUE」結尾,後一個以「CALL_FUNCTION STORE_FAST LOAD_FAST RETURN_VALUE」結尾。 – Hyperboreus
@Hyperboreus:Python解釋器不允許優化嗎? – ruakh
我不確定是否可以優化創建局部變量。我現在不允許他們這樣做。我的不是。我最後的評論來自反彙編。 – Hyperboreus