从列表中删除元素的最佳方法

use*_*054 11 python list

我想知道从列表中删除元素的最佳方法/有效方法是什么.

一些功能被Python提供:

  1. some_list.remove(value),但如果找不到值则抛出错误.
  2. some_list.pop(some_list[index]),删除列表中给定位置的项目,并将其返回.
  3. del (some_list[index]),它从给定索引中删除元素,它与pop不同,因为它不返回值.

场景:

  • 如果要删除的项目很少,请说一个元素或1到5之间.
  • 如果必须删除序列中的多个项目.
  • 如果必须根据条件删除不同的项目.
  • 如果你有一个列表列表并想要按顺序删除元素怎么样?

Jam*_*pam 15

我的回答并不完全是你的问题,但在你读完之后,我希望你能决定你需要选择哪种类型来满足你的需求.

Python的列表是可变长度数组,而不是Lisp样式的链表.该实现使用对其他对象的连续引用数组,并保留指向此数组的指针.

这使得列表的索引成为[i]一种操作,其成本与列表的大小或索引的值无关.

附加或插入项目时,将调整引用数组的大小.应用了一些算法来提高重复附加项的性能; 当必须增长数组时,会分配一些额外的空间,因此接下来的几次不需要实际的大小调整,即过度分配.更多信息

删除vs Pop与删除:

乍一看,看起来所有人都在做同样的事情.

引擎盖下它的表现不同.

删除:通过从0索引迭代直到找到元素的第一个匹配来从列表中删除元素.如果元素结束,则需要更多时间进行迭代.

pop:使用索引从列表中删除元素.花更少的时间.

del:是一个python语句,它使用索引从名称空间或字典中的项目或列表中的项目中删除名称.

去掉:

  • 它消除了第一次出现的价值.
  • 如果值不存在,则引发ValueError.
  • 它只需要一个参数,因此您不能一次删除多个值.

POP:

  • 删除并返回索引处的项目(默认为last).
  • 如果list为空或索引超出范围,则引发IndexError.
  • 它只需要一个参数,因此您不能一次删除多个值.

DEL:

  • 删除索引处的项目并返回任何内容.
  • 它可以从列表中删除切片或清除整个列表.

基准:

最坏的情况:从列表末尾删除.

yopy:-> python -m timeit "x=range(1000)" "x.pop(999)"
100000 loops, best of 3: 10 usec per loop
yopy:-> python -m timeit "x=range(1000)" "x.remove(999)"
10000 loops, best of 3: 31.3 usec per loop
yopy:-> python -m timeit "x=range(1000)" "del x[999]"
100000 loops, best of 3: 9.86 usec per loop
yopy:->
Run Code Online (Sandbox Code Playgroud)

最佳案例:开始列表.

yopy:-> python -m timeit "x=range(1000)" "x.remove(1)"
100000 loops, best of 3: 10.3 usec per loop
yopy:-> python -m timeit "x=range(1000)" "x.pop(1)"
100000 loops, best of 3: 10.4 usec per loop
yopy:-> python -m timeit "x=range(1000)" "del x[1]"
100000 loops, best of 3: 10.4 usec per loop
yopy:->
Run Code Online (Sandbox Code Playgroud)

需要注意的是:

如果数组在中间增长或缩小

  • Realloc仍然取决于总长度.
  • 但是,必须复制所有尾随元素

所以,现在我希望你能决定你需要选择什么来满足你的需求.

  • 请注意,上述代码片段仅适用于 Python 2.x,其中 [`range()`](https://docs.python.org/2/library/functions.html#range) 生成实际的整数列表。对于 Python 3.x,该函数返回一个与实际序列不同的 Range 对象。 (2认同)

Ash*_*ary 5

使用列表理解

方案1:

[item for item in my_list if 1 <= item <=5 ]
Run Code Online (Sandbox Code Playgroud)

方案2:

to_be_removed = {'a', '1', 2}
[item for item in my_list if item not in to_be_removed ]
Run Code Online (Sandbox Code Playgroud)

方案3:

[item for item in my_list if some_condition()]
Run Code Online (Sandbox Code Playgroud)

场景4(嵌套列表理解):

[[item for item in seq if some_condition] for seq in my_list]
Run Code Online (Sandbox Code Playgroud)

请注意,如果要删除只是一个项目,然后list.removelist.pop并且del肯定会是非常快的,但是使用这些方法,同时遍历列表中可能会导致意外的输出。

相关:循环“忘记”以删除一些项目

  • @ user3247054具有内置函数,例如:`filter(None,my_list)`,`filter(str.isupper,my_list)`等,但是在Python 3中,您将不得不使用附加的列表调用,因为它返回一个那里的迭代器。要在Python 2中获得迭代器,可以使用`itertools.ifilter`。 (2认同)

Loï*_*oix 5

使用过滤器而不是列表理解:

场景1:

filter(lambda item: 1 <= item <= 5, my_list)
Run Code Online (Sandbox Code Playgroud)

场景2:

to_be_removed = {'a', '1', 2}
filter(lambda item: item not in to_be_removed, my_list)
Run Code Online (Sandbox Code Playgroud)

场景3:

filter(lambda item: some_condition(), my_list)
Run Code Online (Sandbox Code Playgroud)

场景4(嵌套过滤列表):

filter(lambda seq: filter(lambda item: some_condition(), seq), my_list) 
Run Code Online (Sandbox Code Playgroud)

出于某种原因,它与列表comprhension是一样的,但很明显我们正在过滤事物而不是生成它们.


Jen*_*ens 5

好问题,James 的回答是唯一一个针对某些建议方法提供 Python 2.x 实际性能数据的答案。(另见对该问题的评论。)

为了完成 Python 3.x 的图片,这里还有一些测试。因为单个测试可能会修改它的列表,所以我们需要N个列表来修改N个测试;因此我在运行测试之前创建了一组列表。

# Python 3.6.2 (default, Jul 18 2017, 14:13:41) 
>>> import timeit
>>> number = 10000   # Number of tests.
>>> # Generate `number` lists of 1000 integer elements.
>>> setup = """
... lists=[[_ for _ in range(1000)] for _ in range(10000)]
... i = 0
... """
>>>
Run Code Online (Sandbox Code Playgroud)

所有测试,无论它们是修改列表而不是生成新列表,都会迭代该列表集,以确保测试的条件相同。为简单起见,所有测试都从列表中间删除单个元素。

让我们从使用内置 list() 函数的问题中的示例开始:

# remove()
>>> stmt = """
... l = lists[i]     # Get the current work list.
... l.remove(500)    # Remove element.
... i += 1           # On to the next list.
... """
>>> timeit.timeit(stmt, setup=setup, number=number)
0.08474616194143891

# pop()
>>> stmt = "l = lists[i]; l.pop(500); i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
0.01088976499158889

# index() and pop()
>>> stmt = "l = lists[i]; l.pop(l.index(500)); i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
0.08841867197770625

# del
>>> stmt = "l = lists[i]; del l[500]; i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
0.008702976978383958

# index() and del
>>> stmt = "l = lists[i]; del l[l.index(500)]; i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
0.08238211390562356
Run Code Online (Sandbox Code Playgroud)

列出Ashwini Chaudhary 的回答中概述的理解:

>>> stmt = "l = lists[i]; [_ for _ in l if _ != 500]; i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
0.44951551605481654
Run Code Online (Sandbox Code Playgroud)

使用Loïc Faure-Lacroix's answer 中概述的 filter() 。但是请注意,上述答案中的示例返回filterPython 3.x的对象,而不是像 Python 2.x 那样的列表!

# Generate a filter object.
>>> stmt = "l=lists[i]; filter(lambda _: _ != 500, l); i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
0.0031418869039043784

# Generate a list from the filter object.
>>> stmt = "l=lists[i]; list(filter(lambda _: _ != 500, l)); i += 1"
>>> timeit.timeit(stmt, setup=setup, number=number)
1.1863253980409354
Run Code Online (Sandbox Code Playgroud)

使用 Python 的内置函数删除存在的元素需要额外的测试;列表理解和过滤器解决方案优雅地处理不存在的列表元素。

# Catch a resulting exception.
>>> stmt = """
... l = lists[i]
... try:
...     del l[l.index(1234)]
... except ValueError:
...     pass
... i += 1
... """
>>> timeit.timeit(stmt, setup=setup, number=number)
0.1451275929575786

# Test if the element exists, then delete.
>>> stmt = """
... l = lists[i]
... if 1234 in l:
...     del l[l.index[1234]]
... i += 1
... """
>>> timeit.timeit(stmt, setup=setup, number=number)
0.13344507792498916
Run Code Online (Sandbox Code Playgroud)

我希望我做对了……