【问题标题】:Is removing a list element by index more efficient than removing it by value?按索引删除列表元素比按值删除更有效吗?
【发布时间】:2015-08-18 16:41:12
【问题描述】:

在 python 中,假设我们有以下列表: my_list = ['a', 'b', 'c', 'd', ...]

my_list.pop(3) 会比my_list.remove('d') 更有效率吗?

【问题讨论】:

  • 你试过用timeit自己计时吗?
  • 仅仅因为在某些任意输入下效果更好并不意味着它更有效。我正在寻找更理论的答案。
  • 它们不等价 - 您是否已经知道索引或项目?我希望通过移动阵列中的其他所有内容来控制任何细微的差异。

标签: python performance runtime python-internals


【解决方案1】:

根据this answer,这两种方法的复杂度是:

pop     O(n - i)
remove  O(n)

列表长度为n,元素索引为i

因此,根据您列表的大小,pop可能从长远来看会更快。

【讨论】:

  • 不过,实际上,我怀疑您是否会以n 的任何日常值击败常数项。
【解决方案2】:

对于小列表来说没关系:

[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

如果您的列表非常大,或者比较列表中的元素需要很长时间,那么差异可能会更大。 Remove 会比较慢,因为它必须遍历(并比较)所有元素:

[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

【讨论】:

    【解决方案3】:

    查看listpoplistremove 的实际C 代码(您的意思是CPython,对吗?),您可以看到:

    1. .remove 需要遍历列表(因此按O(i) 缩放,其中i 是项目的索引);

    2. .pop 走捷径,如:

      • 确定索引是否超出范围(第 928 行)很简单,但 .remove 必须检查整个列表以找到它的目标(或不这样做);和

      • 它将索引作为列表中最后一项的特殊情况(第 933 行);

    3. listremove有一个对PyObject_RichCompareBool的调用(第2200行),相对开销较大,因为需要检查当前索引处的对象是否等于item;和

    4. 一旦找到合适的切片位置,两者(除了上面提到的 .pop 的特殊情况)最终都委托给 list_ass_slice(第 941 和 2202 行 - 后面没有咯咯笑);这必须将数组其余部分中的项目打乱,所以将是O(n - i)

    在此基础上.pop 会更快,尤其是对于列表中的最后一项;但是,如果您从项目本身开始,并且已经进行了 O(n) 操作并进行了丰富的比较以找到它的 .index,那么您在回旋处已经失去的摆动中获得了什么。

    此外,重新排列数组中剩余的所有内容(即list_ass_slice)以弥补您删除的内容(.pop.remove 都需要做的操作)可能要多得多比首先弄清楚要删除哪个项目要昂贵。

    请注意,在不深入研究源代码的情况下,只要从逻辑上思考每个操作涉及的内容,就可以弄清楚以上所有内容。

    【讨论】:

      猜你喜欢
      • 2017-12-16
      • 2020-01-02
      • 2020-09-27
      • 1970-01-01
      • 2010-10-12
      • 2021-12-29
      • 2010-12-16
      相关资源
      最近更新 更多