python的list添加新元素的操作效率比較


  List是python中比較常用的數據類型,一般向list添加元素有如下四種方法(append,extend,insert,+),自己寫了個簡單方法,向一個空的list添加3千萬個相同元素,比較了下這四種方法的效率: 

import time
def append(n):
a=[]
for i in range(n):
a.append(1)

def insert(n):
b=[]
for i in range(n):
b.insert(i, 1)
def extend(n):
c=[]
for i in range(n):
c.extend((1,))
def sum(n):
d=[]
list = [1]
for i in range(n):
list1 = d + list
num = 30000000
intervall1 = time.time()
append(num)
print('append',time.time()-intervall1)

intervall2 = time.time()
insert(num)
print('insert',time.time()-intervall2)

intervall3 = time.time()
extend(num)
print('extend',time.time()-intervall3)

intervall4 = time.time()
sum(num)
print('sum',time.time()-intervall4)

 

運行結果:

(sum代表+方法,下面簡稱sum)

append 2.937000036239624(秒)
insert 5.21399998664856(秒)
extend 2.991000175476074(秒)
sum 2.7809998989105225(秒)

從結果看來sum的效率最高,insert效率最低,append和extend效率基本一致。

  1.首先sum方法不同於前三種方法,前三種方法添加元素時是在原來的list對象進行操作,他們沒有返回值。而sum方法是將兩個list對象連接,生成一個新的list對象,從而消耗額外的內存。(對於io密集型應用不建議用該方法,況且針對append方法來說效率提升有限)

  2. append() 追加單個元素到List的尾部,只接受一個參數,參數可以是任何數據類型,被追加的元素在List中保持着原結構類型。此元素如果是一個list,那么這個list將作為一個整體進行追加,注意append()和extend()的區別。

>>> list1=['a','b']
>>> list1.append('c')
>>> list1
['a', 'b', 'c']

   3.extend() 將一個列表中每個元素分別添加到另一個列表中,只接受一個參數;extend()相當於是將list B 連接到list A上。

>>> list1
['a', 'b', 'c']
>>> list1.extend('d')
>>> list1
['a', 'b', 'c', 'd']

 4.insert() 將一個元素插入到列表中,但其參數有兩個(如insert(1,”g”)),第一個參數是索引點,即插入的位置,第二個參數是插入的元素。

>>> list1
['a', 'b', 'c', 'd']
>>> list1.insert(1,'x')
>>> list1
['a', 'x', 'b', 'c', 'd']

  為什么人們普遍說append效率要高於insert,因為Python中的列表並不是傳統意義上的列表,這也是Python中列表的append操作比insert操作高效的根本原因。傳統意義上的列表,通常叫做鏈表,是通過一系列的節點來實現的,每個節點(尾節點除外)都有一個指向下一個節點的指針。簡單單向鏈表(雙向鏈表多一個指向前一個節點的指針)實現如下:
class Node:
def __init__(self,value,next=Node)
self.value=value
self.next=next
將節點構造成列表:
>>>L=Node("a",Node("b",Node("c",Node("d"))))
>>>L.next.next.value
'c'
  但是Python中的列表則與此不同。它不是由若干個獨立的節點相互引用而組成的,而是一整塊單一連續的內存區塊--我們通常稱之為數組。這直接導致其與鏈表之間的一些區別。例如,盡管兩者在遍歷時的效率相差無幾(除了鏈表有一些額外開銷),但是如果我們按照既定索引值對某元素進行直接訪問的話,顯然使用數組會更加的高效。因為在數組中,我們通常可以直接計算出目標元素在內存中的位置,並對其進行直接訪問。而對於鏈表,我們需要從頭開始遍歷整個鏈表。
  但是對於insert操作來說,情況又有所不同。對於鏈表而言,只要知道了在哪里執行insert操作,其操作成本是非常低的。無論該鏈表中有多少元素,其操作時間大致相同。但是,對於數組而言,每次執行insert操作都需要移動插入點右邊所有的元素,甚至在必要時需要把所有數組元素復制到另外一個更大的數組中。也正因如此,append操作通常會采用一種被稱為動態數組或向量的特定解決方案。其主要思路是將內存分配的過大一些,並且等到其溢出時,在線性時間內再次重新分配內存。但這樣做似乎會使得append操作變得跟insert操作一樣糟糕。其實不然,因為盡管這兩種情況會迫使我們去搬動大量的元素,但主要的不同點在於,對於append操作,發生這樣的可能性要小得多。事實上,只要我們能確保每次所搬入的數組都大過原數組一定的比例(例如20%甚至100%),那么該操作的平均成本(或者說的更確切一些,將這些搬運開銷均攤到每次append操作中去)通常是常數的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM