从列表中删除与子字符串匹配的项

32

如果要删除列表中与子字符串匹配的元素,该怎么做?

我尝试使用pop()enumerate方法从列表中删除元素,但似乎错过了一些需要删除的连续项:

sents = ['@$\tthis sentences needs to be removed', 'this doesnt',
     '@$\tthis sentences also needs to be removed',
     '@$\tthis sentences must be removed', 'this shouldnt',
     '# this needs to be removed', 'this isnt',
     '# this must', 'this musnt']

for i, j in enumerate(sents):
  if j[0:3] == "@$\t":
    sents.pop(i)
    continue
  if j[0] == "#":
    sents.pop(i)

for i in sents:
  print i

输出:

this doesnt
@$  this sentences must be removed
this shouldnt
this isnt
#this should
this musnt

期望的输出:

this doesnt
this shouldnt
this isnt
this musnt

3
在遍历列表时,移除其中的元素是一个经典问题。请阅读其他 Stack Overflow 相关问题的答案。此外,参见文档中的注释。 - John Y
1
在迭代容器时,应始终避免更改其长度,否则会引发灾难。 - wim
通常情况下,创建一个新的过滤列表比尝试就地修改列表要好。不可变算法总是更容易理解(虽然并不总是更容易编写)。当你只是替换值时,有时就地操作的效率优势会胜过它,但当你在列表中间删除或插入时,通常会得到更差的效率以及不太健壮的逻辑。 - abarnert
3个回答

46
如何尝试一些简单的东西,比如:
>>> [x for x in sents if not x.startswith('@$\t') and not x.startswith('#')]
['this doesnt', 'this shouldnt', 'this isnt', 'this musnt']

19

这应该可以正常工作:

[i for i in sents if not ('@$\t' in i or '#' in i)]

如果您只想要以特定句子开头的东西,请使用str.startswith(stringOfInterest)方法。
[i for i in sents if i.startswith('#')]

4
我认为这个比起另外两个更好,因为它不假设子字符串在开头。 - Frikster
请注意 -- 如果您在使用此代码时遇到 NoneType 错误,请检查您的值并确保从列表中删除任何 <class 'NoneType'> 值。参考链接:https://www.geeksforgeeks.org/python-remove-none-values-from-list/ - JayRizzo

14

另一种使用filter的技巧

filter( lambda s: not (s[0:3]=="@$\t" or s[0]=="#"), sents)

你原来的做法有个问题,当你处理列表项i并决定删除它时,你会把它从列表中移除,这就导致i+1项会滑动到i位置。在下一次循环迭代中,你会处于索引i+1位置,但实际上该项是i+2。明白了吗?

谢谢解释!在枚举时弹出列表确实很傻。哈哈哈。。 - alvas
只有这个对我起作用:sents = list(filter( lambda s: not (s[0:3]=="@$\t" or s[0]=="#"), sents)) - chainstair

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接