通过使列表等于一个切片或使用 del 来截断列表是否更快?
Is it faster to truncate a list by making it equal to a slice, or by using del?
假设我有一个 list
TruncList
,其中的元素数量大于 n
。如果我想从该列表的末尾删除 n
元素, 将列表重新定义为自身的一部分 保留所需元素是否更快,如 TruncList = TruncList[:-n]
,或从列表中删除不需要的元素,如del TruncList[-n:]
?
如果我从 TruncList
中删除 first n
元素,答案是否会改变,如 TruncList = TruncList[n:]
与 del TruncList[:n]
?
除了速度之外,这些方法中的一种是否比另一种更符合 Pythonic?
我认为重新定义方法可能会更慢,因为它遍历 TruncList
然后重新分配它,而 del
会在适当的位置截断列表,但我不确定是否这些都是这样。
我还认为 del
是更好的路线,因为它似乎是该功能的自然用法。
所以我自己使用 timeit
和这些样本进行了测试:
## Make a list of 500 elements and then remove the first 80...
def slice_front():
"Make the list equal to all but the first eighty elements."
trunc = 80
TruncList = range(500)
TruncList = TruncList[trunc:]
def del_front():
"Use del to remove the first eighty elements."
trunc = 80
TruncList = range(500)
del TruncList[:trunc]
## Make a list of 500 elements and then remove the last 80...
def slice_end():
"Make the list equal to all but the last eighty elements."
trunc = 80
TruncList = range(500)
TruncList = TruncList[:-trunc]
def del_end():
"Delete the last eighty elements from the list using del."
trunc = 80
TruncList = range(500)
del TruncList[-trunc:]
...并得到这些结果:
>>> timeit.timeit(slice_front, number = 66666)
1.3381525804258112
>>> timeit.timeit(del_front, number = 66666)
1.0384902281466895
>>> timeit.timeit(slice_end, number = 66666)
1.3457694381917094
>>> timeit.timeit(del_end, number = 66666)
1.026411701603827
看起来 del
更快,而且差距很大。
编辑
如果我 运行 相同的样本,但用 trunc = 2
代替,结果如下:
>>> timeit.timeit(slice_front, number = 66666)
1.3947686585537422
>>> timeit.timeit(del_front, number = 66666)
1.0224893312699308
>>> timeit.timeit(slice_end, number = 66666)
1.4089230444569498
>>> timeit.timeit(del_end, number = 66666)
1.042288032264116
del
还是更快。
这是一个几乎所有列表元素都被删除的测试:trunc = 80
和 TruncList = range(81)
...
>>> timeit.timeit(slice_front, number = 66666)
0.25171681555993247
>>> timeit.timeit(del_front, number = 66666)
0.2696609454136185
>>> timeit.timeit(slice_end, number = 66666)
0.2635454769274057
>>> timeit.timeit(del_end, number = 66666)
0.294670910710936
在这种情况下,del
比重新定义方法要慢一些。
这完全取决于您删除了多少元素。
在 CPython 中,list
类型使用动态过度分配策略来避免必须过于频繁地调整底层 C 数组的大小。有一个 array
来保存元素,它始终保持稍微太大。
删除然后(使用del TruncList[-n:]
)可能是一个几乎免费的操作,前提是n
足够小。事实上,在调整大小发生之前,您最多可以安全地删除过度分配数组大小的 一半 。调整大小需要将所有现有引用复制到新数组。
使用切片 总是 创建新的列表对象,需要分配内存并复制所涉及的元素。这比重新分配数据稍微多一些工作。
因此,在不测量时间性能(使用 timeit
)的情况下,我希望 del
选项比切片更快;在 n < len(TruncList) // 2
(小于长度的一半)的情况下,在许多情况下你甚至不会导致调整大小,即使你这样做了,需要做的工作也略少,因为只需要重新创建内部数组.
当您从前面删除项目时,您将始终必须重新创建内部数组。那时差异不会很明显,但是创建切片仍然会导致分配一个全新的对象。
假设我有一个 list
TruncList
,其中的元素数量大于 n
。如果我想从该列表的末尾删除 n
元素, 将列表重新定义为自身的一部分 保留所需元素是否更快,如 TruncList = TruncList[:-n]
,或从列表中删除不需要的元素,如del TruncList[-n:]
?
如果我从 TruncList
中删除 first n
元素,答案是否会改变,如 TruncList = TruncList[n:]
与 del TruncList[:n]
?
除了速度之外,这些方法中的一种是否比另一种更符合 Pythonic?
我认为重新定义方法可能会更慢,因为它遍历 TruncList
然后重新分配它,而 del
会在适当的位置截断列表,但我不确定是否这些都是这样。
我还认为 del
是更好的路线,因为它似乎是该功能的自然用法。
所以我自己使用 timeit
和这些样本进行了测试:
## Make a list of 500 elements and then remove the first 80...
def slice_front():
"Make the list equal to all but the first eighty elements."
trunc = 80
TruncList = range(500)
TruncList = TruncList[trunc:]
def del_front():
"Use del to remove the first eighty elements."
trunc = 80
TruncList = range(500)
del TruncList[:trunc]
## Make a list of 500 elements and then remove the last 80...
def slice_end():
"Make the list equal to all but the last eighty elements."
trunc = 80
TruncList = range(500)
TruncList = TruncList[:-trunc]
def del_end():
"Delete the last eighty elements from the list using del."
trunc = 80
TruncList = range(500)
del TruncList[-trunc:]
...并得到这些结果:
>>> timeit.timeit(slice_front, number = 66666)
1.3381525804258112
>>> timeit.timeit(del_front, number = 66666)
1.0384902281466895
>>> timeit.timeit(slice_end, number = 66666)
1.3457694381917094
>>> timeit.timeit(del_end, number = 66666)
1.026411701603827
看起来 del
更快,而且差距很大。
编辑
如果我 运行 相同的样本,但用 trunc = 2
代替,结果如下:
>>> timeit.timeit(slice_front, number = 66666)
1.3947686585537422
>>> timeit.timeit(del_front, number = 66666)
1.0224893312699308
>>> timeit.timeit(slice_end, number = 66666)
1.4089230444569498
>>> timeit.timeit(del_end, number = 66666)
1.042288032264116
del
还是更快。
这是一个几乎所有列表元素都被删除的测试:trunc = 80
和 TruncList = range(81)
...
>>> timeit.timeit(slice_front, number = 66666)
0.25171681555993247
>>> timeit.timeit(del_front, number = 66666)
0.2696609454136185
>>> timeit.timeit(slice_end, number = 66666)
0.2635454769274057
>>> timeit.timeit(del_end, number = 66666)
0.294670910710936
在这种情况下,del
比重新定义方法要慢一些。
这完全取决于您删除了多少元素。
在 CPython 中,list
类型使用动态过度分配策略来避免必须过于频繁地调整底层 C 数组的大小。有一个 array
来保存元素,它始终保持稍微太大。
删除然后(使用del TruncList[-n:]
)可能是一个几乎免费的操作,前提是n
足够小。事实上,在调整大小发生之前,您最多可以安全地删除过度分配数组大小的 一半 。调整大小需要将所有现有引用复制到新数组。
使用切片 总是 创建新的列表对象,需要分配内存并复制所涉及的元素。这比重新分配数据稍微多一些工作。
因此,在不测量时间性能(使用 timeit
)的情况下,我希望 del
选项比切片更快;在 n < len(TruncList) // 2
(小于长度的一半)的情况下,在许多情况下你甚至不会导致调整大小,即使你这样做了,需要做的工作也略少,因为只需要重新创建内部数组.
当您从前面删除项目时,您将始终必须重新创建内部数组。那时差异不会很明显,但是创建切片仍然会导致分配一个全新的对象。