簡體   English   中英

我無法解釋的列表附加和+運算符之間的時間差異

[英]Difference in time between list append and + operator that I can't explain

我正在嘗試在Windows 7上的Python 3.4中使用time.clock()來比較append+=的速度。 (這是Think Python第10章的練習。)這是我的代碼:

import time
fin = open('words.txt')
print("Comparing the time it takes to make a list with append vs. the + operator")

timeConcatStart = time.clock()
wordsList = []
for line in fin:
    word = line.strip()
    wordsList += word
timeConcatEnd = time.clock()
concatElapsed = timeConcatEnd - timeConcatStart
print("+ operator took ",concatElapsed,"seconds for words.txt")

timeAppendStart = time.clock()
wordList = []
for line in fin:
    word = line.strip()
    wordList.append(word)
timeAppendEnd = time.clock()
appendElapsed = timeAppendEnd - timeAppendStart
print("Append took ",appendElapsed,"seconds for words.txt")

在我的系統上,我知道+運算符花費了約0.2秒, append花費了0.002秒。

當我切換順序時,首先是附加代碼塊, append現在需要0.2秒,而+運算符需要0.002秒。

如果我復制並粘貼代碼,兩者都會變得更快,“第三”塊花費約8e-5秒,而“第四”塊花費〜4e5。 然后,如果我再次復制並粘貼,兩者都需要大約5e-5秒。 更令人困惑的是,如果我擺脫了所有復制和粘貼的代碼,然后僅將變量名wordsList的第二次出現更改為otherList,則對於otherList而言,它實際上要更快 即,我得到的時間是第一個塊為0.2秒,而第二個塊為〜5e-5。

為什么第一個循環總是需要最長的時間? open對象是否將words.txt文件加載到內存中,然后將其保存在下次需要的位置? 我以為有一個隱式的“關閉”。

還是與文件I / O無關,而是與分配給列表的內存有關? 即是因為我在第一個循環中“預分配”了wordsList,然后在第二個循環中將其覆蓋了? 我嘗試更改變量名稱以測試該假設,然后得到了更短的時間。

最后,您將如何測試/調試這些想法? 我應該插入打印語句來獲取變量的地址或其他內容嗎? 我是一個調試菜鳥。 請隨時更正我制作的任何術語,並感謝您的幫助

這里發生的事情是您用盡了file對象 文件對象使用當前文件位置從文件中讀取下一個字節,並循環遍歷文件,從文件中讀取文件直至結束。

由於文件仍位於結束位置,因此第二個循環沒有任何結果 您可以for line in fin:循環的第二for line in fin:之前使用fin.seek(0)再次從文件開頭開始讀取,但是這里還有其他問題。

您正在測試操作系統和硬件可以多快地傳輸文件數據。 在進行基准測試時,請盡可能多地消除外部因素。 例如,操作系統會將讀取的文件數據緩存在內存中一段時間​​,這肯定會使結果傾斜。

另一個問題是,每種方法只運行一次,使基准測試容易受到其他偏見的影響。 計算機上運行的其他進程也需要時間,並且可能不公平地影響結果。

為了避免此類偏差,Python附帶了一個稱為timeit的基准測試模塊,該模塊通過多次運行測試(默認值為100萬次),為您的操作系統選擇最准確的計時機制以及禁用潛在的偏差來避免陷阱。作為垃圾收集系統。 您應該在開始時為其提供完全相同的測試數據(在內存中,而不是文件中)。

最后但並非最不重要的一點是,您正在比較錯誤的東西。 +=list.append() 在這里, +=在功能上等效於list.extend() 該列表擴展了添加序列中的每個元素, 而不是單個元素:

>>> lst = []
>>> lst += 'foobar'
>>> lst
['f', 'o', 'o', 'b', 'a', 'r']
>>> lst.append('foobar')
>>> lst
['f', 'o', 'o', 'b', 'a', 'r', 'foobar']

這樣, list.append()將變得更快,因為在這種情況下一次僅添加一個元素:

>>> from timeit import timeit
>>> timeit('lst.append(value)', 'lst = []; value = "foobar"')
0.07227020798018202
>>> timeit('lst += value', 'lst = []; value = "foobar"')
0.15380392200313509

第一個測試將字符串'foobar'添加到lst一百萬次,第二個測試通過添加單個元素'f''o''o''b''a''r'一百萬次。 毫不奇怪,第二次測試的運行速度不到一半。

如果要使用+=一個元素添加到列表中,則需要將該元素包裝為另一序列。 單例列表對象,例如:

>>> timeit('lst += [value]', 'lst = []; value = "foobar"')
0.14356198499444872

這仍然比使用list.append()慢得多,但是至少現在您達到了相同的最終結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM