迭代与列表连接
Posted
技术标签:
【中文标题】迭代与列表连接【英文标题】:Iterating vs List Concatenation 【发布时间】:2013-07-04 23:51:06 【问题描述】:因此,有两种方法可以获取一个列表并将第二个列表的成员添加到第一个列表中。您可以使用列表连接,也可以对其进行迭代。你可以:
for obj in list2:
list1.append(obj)
或者你可以:
list1 = list1 + list2
或
list1 += list2
我的问题是:哪个更快,为什么?我使用两个非常大的列表(超过 10000 个对象)对此进行了测试,似乎迭代方法比列表连接(如 l1 = l1 + l2)快得多。为什么是这样?有人可以解释吗?
【问题讨论】:
它们根本不是一回事。 在询问有关计时的问题时,最好展示一个可重现的计时版本:既能显示比较的细节,又能让其他人抢先查看。跨度> 那么list1.extend(list2)
呢?
【参考方案1】:
append
每次添加一项,这是其缓慢的原因,以及对append
的重复函数调用。
然而在这种情况下,+=
运算符是 不是+
的语法糖。 +=
运算符实际上并没有创建一个新列表然后将其分配回去,它修改了左边的操作数。使用timeit
可以同时使用 10,000 次,这一点非常明显。
>>> timeit.timeit(stmt="l = l + j", setup="l=[1,2,3,4]; j = [5,6,7,8]", number=10000)
0.5794978141784668
>>> timeit.timeit(stmt="l += j", setup="l=[1,2,3,4]; j = [5,6,7,8]", number=10000)
0.0013298988342285156
+=
更快(大约 500 倍)
您还可以使用 extend
列表方法,该方法可以将任何可迭代(不仅仅是另一个列表)附加到类似 l.extend(l2)
的内容中
>>> timeit.timeit(stmt="l.extend(j)", setup="l=[1,2,3,4]; j = [5,6,7,8]", number=10000)
0.0016009807586669922
>>> timeit.timeit(stmt="for e in j: l.append(e)", setup="l=[1,2,3,4]; j = [5,6,7,8]", number=10000)
0.00805807113647461
逻辑上等同于追加,但速度要快得多。
所以解释一下:
迭代比+
快,因为+
必须构造一个全新的列表
extend
比迭代更快,因为它是一个内置的列表方法并且已经过优化。逻辑上等价于重复追加,但实现方式不同。
+=
比extend
快,因为它可以就地修改列表,知道列表必须有多大,并且无需重复的函数调用。它假设您将列表附加到另一个列表/元组
【讨论】:
+=
显然不进行方法查找(基于dis
ooutput)。
@Aristides +=
的行为是在类的 __iadd__
方法中定义的,因此类可以用它做任何事情。
使用collections.deque
也很快,但不如+=
用于列表的快。您可能希望在测量比较中添加此 timeit.timeit(stmt="l+=j", setup="from collections import deque; l=deque([1,2,3,4]); j=[5,6,7,8]", number=10000)
。【参考方案2】:
我运行了以下代码
l1 = list(range(0, 100000))
l2 = list(range(0, 100000))
def t1():
starttime = time.monotonic()
for item in l1:
l2.append(item)
print(time.monotonic() - starttime)
l1 = list(range(0, 100000))
l2 = list(range(0, 100000))
def t2():
starttime = time.monotonic()
global l1
l1 += l2
print(time.monotonic() - starttime)
得到了这个,这表明添加列表 (+=) 更快。
0.016047026962041855
0.0019438499584794044
【讨论】:
顺便说一句,运行 python 3.3。结果更有意义,因为迭代所有内容听起来不像是聚会。【参考方案3】:你测量错误;多次迭代和调用append
比一次调用要慢得多,因为许多函数调用(至少在 cpython 中)的开销使与实际列表操作有关的任何事情都相形见绌,如 cPython 2.7.5 所示在 Linux x64 上:
$ python -m timeit -s 'x = range(10000);y = range(10000)' 'for e in y:x.append(e)'
100 loops, best of 3: 2.56 msec per loop
$ python -m timeit -s 'x = range(10000);y = range(10000)' 'x = x + y'
100 loops, best of 3: 8.98 msec per loop
$ python -m timeit -s 'x = range(10000);y = range(10000)' 'x += y'
10000 loops, best of 3: 105 usec per loop
$ python -m timeit -s 'x = range(10000);y = range(10000)' 'x.extend(y)'
10000 loops, best of 3: 107 usec per loop
请注意,x = x + y
创建了列表的第二个副本(至少在 cPython 中)。 x.extend(y)
及其表亲 x += y
执行与多次调用 append
相同的操作,只是没有实际调用 Python 方法的开销。
【讨论】:
好笑。从命令行测量,+=
和 extend
完全相同,而从交互式解释器测量,+=
明显更快。 (python3.3)以上是关于迭代与列表连接的主要内容,如果未能解决你的问题,请参考以下文章