[英]Is removing a list element by index more efficient than removing it by value?
在python中,假设我们有以下列表: my_list = ['a', 'b', 'c', 'd', ...]
my_list.pop(3)
是否比my_list.remove('d')
更有效?
对于小的列表来说没关系:
[wander@box ~]$ python -m timeit '[1, 2, 3].pop(1)'
10000000 loops, best of 3: 0.167 usec per loop
[wander@box ~]$ python -m timeit '[1, 2, 3].remove(2)'
10000000 loops, best of 3: 0.129 usec per loop
如果您的列表确实很大,或者比较列表中的元素需要花费很长时间,则可能会有更多的不同。 删除会比较慢,因为它必须遍历(并比较)所有元素:
[wander@box ~]$ python -m timeit -n 100000 'list(range(1, 100)).pop(98)'
100000 loops, best of 3: 0.916 usec per loop
[wander@box ~]$ python -m timeit -n 100000 'list(range(1, 100)).remove(98)'
100000 loops, best of 3: 2.05 usec per loop
这就是整数,它们的比较速度非常快。 如果列表中的元素具有更有趣的__eq__
方法,则remove
可能需要很长时间:
class Foo:
def __eq__(self, other):
time.sleep(1)
return False
[Foo(), Foo(), Foo(), Foo(), 20].remove(20)
因此,如果您知道索引,请使用pop
。
查看listpop
和listremove
的实际C代码(您打算使用CPython,对吗?),您可以看到:
.remove
需要遍历列表(因此,按O(i)
进行缩放,其中i
是项目的索引);
.pop
采用快捷方式,例如:
弄清楚索引是否超出范围(第928行)很简单,但是.remove
必须检查整个列表以找到目标(或者不这样做); 和
特殊情况下,索引是列表中的最后一项(第933行);
listremove
调用了PyObject_RichCompareBool
(第2200行),这是相对昂贵的,因为它需要检查当前索引处的对象是否等于该项目。 和
一旦找到合适的切片位置,两者(除了上面提到的.pop
的特殊情况除外)最终都委托给list_ass_slice
(941和2202行-后面没有咯咯笑声); 这必须将数组其余部分中的项目一起拖曳,所以O(n - i)
也会如此。
在此基础上, .pop
会更快,尤其对于列表中的最后一项; 但是,如果您是从项目本身开始的,并且已经进行了O(n)
操作并进行了丰富的比较以找到其.index
,那么您在回旋处已经失去的秋千收益。
同样,重新排列数组中剩余的所有内容(即list_ass_slice
)以弥补您已删除的内容( .pop
和.remove
需要这样做)的操作可能比找出要删除的项目要昂贵得多。首先删除。
注意,在不深入源代码的情况下,只需从逻辑上考虑每个操作涉及的内容,就可以弄清楚上面的所有内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.