python insert vs append

时间:2011-10-15 09:21:19

标签: python

我已经编写了基本的python片段,首先在列表中插入值然后反转它们。我发现insert和append方法之间的执行速度差异很大。

摘录1:

L = []
for i in range(10**5):
 L.append(i)
L.reverse()

执行此操作所需的时间:

real    0m0.070s
user    0m0.064s
sys         0m0.008s

摘录2:

l = []
for i in range(10**5):
 l.insert(0,i)

执行时间:

real    0m5.645s
user    0m5.516s
sys         0m0.020s

我希望代码片段2的性能比snippet1好得多,因为我之前通过插入数字直接执行反向操作。但是,所用的时间不然。我无法理解为什么后一种方法需要更多时间来执行,即使该方法看起来更优雅。有没有人对此有任何解释?

6 个答案:

答案 0 :(得分:32)

以下是来自 Duncan Booth的完整answer

  

列表由指向对象的指针数组实现   包含。

     

每次调用'insert(0,indx)'时,所有指针都已经存在   列表必须在新位置之前向上移动一次   在开头插入。

     

当你调用'append(indx)'时,只需要复制指针   当前分配的块中没有足够的空间用于新的   元件。如果有空间,则无需复制现有空间   元素,只需将新元素放在最后并更新长度   领域。每当必须为该特定分配新块时   append将不会比插入更快,但是会有一些额外的空间   如果您希望进一步扩展列表,则分配。

     

如果你希望插入更快,也许你认为Python   使用了链表实现。它没有这样做,因为在   实践(对于大多数应用程序)基于列表的实现给出   更好的表现。

我实际上没有别的东西可以补充。

答案 1 :(得分:16)

请注意,您的结果将取决于精确的Python实现。 cpython(和pypy)会自动调整列表大小和 overprovision 空间,以便将来添加,从而加快append的速度。

在内部,列表只是具有恒定大小的内存块(在上)。有时你很幸运,可以增加块的大小,但在很多情况下,一个对象已经存在。例如,假设您为列表[a,b,c,d]分配了大小为4的块,而另一段代码为字典分配了大小为6的块:

Memory  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
       |a b c d| | dictionary |

假设您的列表包含4个元素,并添加另一个元素。现在,您只需将列表大小调整为5:

Memory  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
       |a b c d e| dictionary |

但是,如果现在需要另一个元素,你会怎么做?

嗯,你唯一能做的就是获得一个新的空间并复制列表的内容。

Memory 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
                | dictionary |a  b  c  d  e  f |

请注意,如果您批量获取空间(上述过度配置),您只需要不时地调整(并可能复制)列表。

相反,当您在位置0处插入时,您始终需要复制列表。我们插入x

Memory  0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
orig   |a b c d| |dictionary|
after  |x a b c d|dictionary|

虽然最后有足够的空间来附加x,但我们不得不移动(甚至不是复制,内存可能更便宜)所有其他值。

答案 2 :(得分:5)

如果您需要的数据结构在开始时插入和插入一样有效,那么您应该考虑deque

答案 3 :(得分:2)

我已经学会了在“Python Pocket Reference”中将x插入列表开头的技巧:

l[:0] = [x]

它必须与l.insert(0,x)非常相似,但是当我尝试比较三个选项时:append(x),insert(0,x)和l [:0] = [x],最后一个选项的执行速度比第二个选项快一点。

这是测试代码和结果

import time
def test():
    n = 10**5


    t0 = time.time()
    l = []
    for i in xrange(n): l.append(i)
    t1 = time.time() - t0
    print 'appending: %.5f' % t1


    t0 = time.time()
    l = []
    for i in xrange(n): l.insert(0, i)
    t2 = time.time() - t0
    print 'insert to 0: %.5f' % t2

    t0 = time.time()
    l = []
    for i in xrange(n): l[:0] = [i]
    t3 = time.time() - t0
    print 'set slice: %.5f' % t3

    return t1, t2, t3


if __name__ == '__main__':
    t = [0] * 3
    ntimes = 10

    for _ in xrange(ntimes):
        ti = test()

        for i in xrange(3):
            t[i] += ti[i]

    t = [i/ntimes for i in t]
    print 'average time:', t

平均时间

[0.011755657196044923, 4.1943151950836182, 3.3254094839096071]

为什么它比插入(0,x)快25%?我试图交换估计t1,t2,t3的代码块,但结果是一样的,所以它不是关于缓存列表。

Here,它声明设置切片需要O(k + n)

答案 4 :(得分:0)

在Queue中适当实现插入方法。     FIFO操作,插入列表的前面。     例如:。 items.insert(0,项)

追加在堆栈中正确实现的方法。     LIFO操作,插入列表的末尾。     例如:。 items.append(项目)

当我们通过INSERT方法使用插入数据时,请确保重新排序所有索引。

答案 5 :(得分:0)

https://wiki.python.org/moin/TimeComplexity 去这里查看列表的所有方法及其时间复杂度