在python中,假设我们有以下列表:
my_list = ['a', 'b', 'c', 'd', ...]
my_list.pop(3)
会比my_list.remove('d')
更有效吗?
答案 0 :(得分:8)
答案 1 :(得分:6)
小清单并不重要:
[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
。
答案 2 :(得分:4)
查看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
需要执行的操作,可能比首先确定要删除哪个项目要贵得多。
请注意,如果不深入了解源代码,几乎所有上述内容都可以通过逻辑思考每个操作涉及的内容来解决。