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操作中去)通常是常数的。