Python - 追加VS扩展效率

Inf*_*ity 13 python performance

这是我用Python编写的一些代码:

from math import sqrt
abundant_list = []

for i in range(12,28123+1):
    dividor_list = [1]
    for j in range(2, int(sqrt(i))+1):
        if i%j == 0:
            dividor_list.extend([i/j,j])
    if sum(dividor_list) > i:
        abundant_list.append(i)

print abundant_list
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,代码实际上是尽可能地提高效率.

如果我使用list.append两次,或list.extend只使用一次,有什么区别吗?我知道这可能是微小的差异,但我真的很想知道:)

mgi*_*son 18

import timeit

def append2x(foo):
    foo.append(1)
    foo.append(1)

def extend_lst(foo):
    foo.extend([1,1])

def extend_tup(foo):
    foo.extend((1,1))


l1 = []
l2 = []
l3 = []

print timeit.timeit('append2x(l1)',setup = 'from __main__ import append2x,l1')
print timeit.timeit('extend_lst(l2)',setup = 'from __main__ import extend_lst,l2')
print timeit.timeit('extend_tup(l3)',setup = 'from __main__ import extend_tup,l3')
Run Code Online (Sandbox Code Playgroud)

这是一个简单的基准.我的结果(os-X,10.5.8,core2duo,FWIW):

0.520906925201  #append
0.602569103241  #extend-list
0.357008934021  #extend-tuple
Run Code Online (Sandbox Code Playgroud)

和我的linux盒子一样的结果排序(Ubuntu,x86-64核心i7):

0.307395935059  #append
0.319436073303  #extend-list
0.238317012787  #extend-tuple
Run Code Online (Sandbox Code Playgroud)

对我而言,这表示extend比这更快append,但与创建一个list相比,创建一个相对昂贵tuple


编辑

在下面的注释中指出,由于元组的不变性,解释器可以优化元组的创建(它创建元组一次并反复使用它).如果我们将代码更改为:

def extend_lst(foo):  
    v = 1
    foo.extend([v,v]) 

def extend_tup(foo):
    v = 1
    foo.extend((v,v))
Run Code Online (Sandbox Code Playgroud)

时间几乎相同:

0.297003984451  #append
0.344678163528  #extend-list
0.292304992676  #extend-tuple
Run Code Online (Sandbox Code Playgroud)

虽然tuple仍然一直击败列表版本append,但我所做的所有试验的版本几乎都没有.

我要从中剔除的一件事是,如果你在一个包含所有文字的对象上进行迭代,那么选择tuplea list.如果它不完全由文字组成,那么无论你选择list还是tuple.

  • 元组版本更快,因为你使用的元组是文字因此重新使用(参见字节码),所以它不必一次又一次地构造.使用变量(例如传递一个对象附加到所有函数),差异将减少. (3认同)
  • @delnan - 我正在研究它.我添加了一个解释这个东西的编辑.我通常不会把糟糕的时间留在答案中,但在这种情况下,我觉得保留两个版本以显示文字元组如何比文字列表快得多是有益的,但是当使用变量时它们相对接近. (2认同)

Mar*_*der 11

值得指出的是,这个问题的答案取决于每次迭代时添加的列表/元组的小尺寸.对于较大的列表,扩展显然是优越的(列表与元组没有区别).从mgilson回答开始,我检查了600个项目的集合的行为,而不是2:调用追加600次需要8倍于使用extend()手动定义的列表/元组(即[v,v,v,v,v,v,v...]):

42.4969689846
5.45146393776
5.38034892082
Run Code Online (Sandbox Code Playgroud)

这五秒钟中的大部分实际上是列表/元组创建.在timeit通话之前准备它会延长时间

1.42491698265
0.657584905624
Run Code Online (Sandbox Code Playgroud)

分别用于列表和元组.

对于更现实(更公平)的情况,可以在函数调用中动态生成数据:

import timeit

def append_loop(foo, reps):
    for i in range(reps):
        foo.append(i)

def append_comp(foo, reps):
    [foo.append(i) for i in range(reps)]

def extend_lst(foo, reps):
    foo.extend([i for i in range(reps)])

def extend_tup(foo, reps):
    foo.extend((i for i in range(reps)))

repetitions = 600

print timeit.timeit('append_loop([], repetitions)', setup='from __main__ import append_loop, repetitions')
print timeit.timeit('append_comp([], repetitions)', setup='from __main__ import append_comp, repetitions')
print timeit.timeit('extend_lst([], repetitions)', setup='from __main__ import extend_lst, repetitions')
print timeit.timeit('extend_tup([], repetitions)', setup='from __main__ import extend_tup, repetitions')
Run Code Online (Sandbox Code Playgroud)

(Append通过for-loop和list comprehension实现,以分解两种循环方式之间的效率差异.)

时间是:

53.8211231232
57.1711571217
19.8829259872
28.5986201763
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,扩展列表理解仍然比追加快两倍.此外,元组理解似乎明显慢于列表理解,并且append_comp仅引入不必要的列表创建开销.

  • 后面的(extend_tup)实际上是一个genexp而不是一个元组,这说明了它的缓慢性。 (2认同)