2013-11-22 34 views
2

當我以不同的方式構建列表時,我注意到了一些有趣的行爲。 .append花費的時間比列表解析,這需要比map長更長的時間,如下面的實驗:循環結構中的加速

def square(x): return x**2 

def appendtime(times=10**6): 
    answer = [] 
    start = time.clock() 
    for i in range(times): 
    answer.append(square(i)) 
    end = time.clock() 
    return end-start 

def comptime(times=10**6): 
    start = time.clock() 
    answer = [square(i) for i in range(times)] 
    end = time.clock() 
    return end-start 

def maptime(times=10**6): 
    start = time.clock() 
    answer = map(square, range(times)) 
    end = time.clock() 
    return end-start 

for func in [appendtime, comptime, maptime]: 
    print("%s: %s" %(func.__name__, func())) 

的Python 2.7:

appendtime: 0.42632 
comptime: 0.312877 
maptime: 0.232474 

的Python 3.3.3:

appendtime: 0.614167 
comptime: 0.5506650000000001 
maptime: 0.57115 

現在,我非常清楚python 2.7中的range會建立一個列表,所以我得到爲什麼在相應函數的時間之間存在差距python 2.7和3.3。我更關心的是append,列表理解和map之間的相對時間差異。首先,我認爲這可能是因爲map和列表解析可以讓解釋者知道結果列表的最終大小,這將允許解釋器malloc一個足夠大的C數組來存儲這個數組,名單。按照這個邏輯,列表解析和map應該花費幾乎相同的時間量。但是,時序數據顯示在python 2.7中,listcomps的速度是〜1.36x的一樣快,與append一樣快,map的速度是listcomps的1.34倍。
更奇怪的是,在python 3.3中,listcomps的速度是append的1.12倍,而map實際上比listcomps的要慢。

很明顯,map和listcomps不「玩相同的規則」;很明顯,地圖利用了列表廣告所不具備的功能。
有沒有人可以揭示這些時間價值差異背後的原因?

+4

強制性「你爲什麼不使用'timeit'?這對基準測試來說更好。」評論。 – delnan

+0

@delnan - 我也在想這個:) – mgilson

回答

1

首先,在python3.x中,map返回一個iterable,不是一個列表,因此解釋了那裏的50kx加速。爲了使它成爲一個公平的時機,在python3.x中,你需要list(map(...))

其次,.append會比較慢,因爲每次通過循環,解釋器需要查找列表,然後它需要在列表中查找append函數。這個額外的.append查找不需要與list-comp或map一起發生。

最後,在列表理解的情況下,我認爲在循環的每一個回合都需要查看函數square。使用地圖時,只有當你打電話給地圖時纔會擡頭,這就是爲什麼如果你在列表理解中調用一個函數,map通常會更快。請注意,雖然列表理解通常擊敗maplambda功能。

+0

@ inspectorG4dget - 是的,我增加了更多的嘗試來解釋這種差異。 – mgilson

+0

我已經用'list(map(...))'更新了我的帖子,這看起來更慢。你能解決這個問題嗎? – inspectorG4dget

+0

@ inspectorG4dget - 不確定我可以。這可能是因爲在這種情況下,你實際上需要調用'map(...).__ iter__',然後'__next__'函數很多次(你沒有使用list-comp)。無論如何,你的差異只有〜5%,這是相當小的。 – mgilson