HOWTO · Python
从 Python 列表中删除某元素的所有出现
在本文中,我们将看到如何在 Python 中从一个 List 中删除一个元素的所有出现。
本页内容
Python 列表可以多次存储同一个值,每次出现都是位于列表中各自位置上的独立元素。remove() 只删除第一个匹配项,因此要清除某个值的所有出现需要换一种方法。最直接的方案是列表推导式,它会构建一个只保留所需元素的新列表;filter() 提供了基于谓词的替代方案;而当必须就地修改原列表时,可以使用围绕 remove() 的循环。
使用列表推导式删除所有匹配的元素
列表推导式会保留每个满足条件的元素,并将这些幸存的元素收集到一个新列表中。运行下面的示例:
本节中的代码以及显示的标签或输出仍为英文。
numbers = [1, 2, 3, 4, 2, 5, 2]
target = 2
filtered = [x for x in numbers if x != target]
print(filtered)
print(numbers)
[1, 3, 4, 5]
[1, 2, 3, 4, 2, 5, 2]
条件 x != target 会保留所有不等于 2 的值,因此三个 2 的副本都会从 filtered 中消失,而 numbers 仍保存原始数据。如果想替换原变量而不是保留两个列表,可以把结果赋值回去:numbers = [x for x in numbers if x != target]。绑定到旧列表对象的其他名称仍然会看到未过滤的元素,当列表的所有权属于调用方时,这通常正是你想要的行为。
如果该值从未在列表中出现过,推导式仍会正常运行,并生成一个包含所有原始元素的列表:
numbers = [1, 3, 4, 5]
target = 2
filtered = [x for x in numbers if x != target]
print(filtered)
[1, 3, 4, 5]
这样一来,无需先检查成员资格就可以安全地运行推导式。
使用 filter() 删除所有匹配的元素
filter() 函数会对每个元素应用一个谓词,只产出谓词为真的元素。在 Python 3 中,它返回的是迭代器而不是列表,因此当你需要一个可重复使用的列表时,要把它的结果传给 list()。谓词通常是一个 lambda(小型匿名函数),用于将每个元素与目标值进行比较:
本节中的代码以及显示的标签或输出仍为英文。
numbers = [1, 2, 3, 4, 2, 5, 2]
target = 2
filtered = list(filter(lambda x: x != target, numbers))
print(filtered)
[1, 3, 4, 5]
与推导式一样,filter() 会构建一个新序列而不改动 numbers;如果目标值从未出现,得到的结果就是一个与输入相同的列表。当谓词已经存在或需要复用时,选用 filter();当条件只是 x != target 这样的一次性检查时,优先使用推导式。
使用 remove() 删除所有匹配的元素
remove() 是一个列表方法,它只删除第一个等于参数的元素,并就地修改列表。一次调用永远无法清除重复项,因此要在该值仍然存在时反复调用它:
本节中的代码以及显示的标签或输出仍为英文。
numbers = [1, 2, 3, 4, 2, 5, 2, 6]
target = 2
while target in numbers:
numbers.remove(target)
print(numbers)
[1, 3, 4, 5, 6]
每次迭代都会先为 target in numbers 扫描一遍列表,然后在 remove() 内部再扫描一遍以定位第一个匹配项,因此每删除一个元素,循环都要执行两次搜索。while 条件同时也是安全保障:当不再有匹配项时,测试结果为假,循环停止。用不在列表中的值调用 remove() 会抛出 ValueError,所以成员资格检查——或等价的 numbers.count(target) 守卫——正是让重复删除保持安全的关键。当目标值从一开始就不在列表中时,循环体根本不会执行,列表会保留它的所有元素。
选择合适的方法
- **需要一个过滤后的副本:**使用列表推导式。它只对列表遍历一遍,把条件内联写出,并且不改动原列表。
- **需要复用谓词:**使用
filter()配合lambda或命名函数。结果是惰性迭代器,因此当你需要一个具体的列表时,记得调用list()。 - **需要就地修改:**当其他代码必须在同一个列表对象上观察到变化时,使用
while ... remove()循环。由于每删除一个元素都要付出一次成员资格扫描加一次删除扫描的代价,在匹配项较多的长列表上,它比单遍扫描的推导式做更多工作。
对于常见的列表规模,推导式是写起来最清晰、最快的方案;只有当列表变得很大或删除操作频繁执行时,循环的额外扫描才会变得明显。