如果一个列表项包含“黑名单”中的任何子字符串,如何从列表中删除该项?

4
在Python中,我想要从一个列表中删除任何包含在所谓的“黑名单”中的子字符串的字符串。
例如,假设列表A如下:
A = [ 'cat', 'doXXXg', 'monkey', 'hoBBBrse', 'fish', 'snake']

而列表 B 是:

B = ['XXX', 'BBB']

我该如何获取C列表:

C = [ 'cat', 'monkey', 'fish', 'snake']

我尝试过各种正则表达式和列表推导的组合,但似乎无法使其正常工作。


为什么要使用正则表达式?请参见此链接 - ThaMe90
我非常好奇那些对这个问题投了“踩”的人!!!!!!!!!!!!!!!!! +1 - vks
太棒了,我正好在寻找这个问题的答案!! - thandasoru
2个回答

14
>>> A = [ 'cat', 'doXXXg', 'monkey', 'hoBBBrse', 'fish', 'snake']
>>> B = ['XXX', 'BBB']

以下列表推导式将会起作用

>>> [word for word in A if not any(bad in word for bad in B)]
['cat', 'monkey', 'fish', 'snake']

这个答案应该被接受,因为它是解决 OP 问题的更短、更 Pythonic 的方式,而且不需要额外的模块。 - Haddock-san

9
您可以将黑名单合并成一个表达式:
import re

blacklist = re.compile('|'.join([re.escape(word) for word in B]))

如果匹配,则过滤掉这些单词:

C = [word for word in A if not blacklist.search(word)]

模式中的单词已经被转义(这样,元字符如.将不会被视为元字符,而是作为字面字符对待),并被连接成一系列的|替代选项:

>>> '|'.join([re.escape(word) for word in B])
'XXX|BBB'

演示:

>>> import re
>>> A = [ 'cat', 'doXXXg', 'monkey', 'hoBBBrse', 'fish', 'snake']
>>> B = ['XXX', 'BBB']
>>> blacklist = re.compile('|'.join([re.escape(word) for word in B]))
>>> [word for word in A if not blacklist.search(word)]
['cat', 'monkey', 'fish', 'snake']

这应该比任何显式的成员测试都要更快,特别是当你的黑名单中单词数量增加时:

>>> import string, random, timeit
>>> def regex_filter(words, blacklist):
...     [word for word in A if not blacklist.search(word)]
... 
>>> def any_filter(words, blacklist):
...     [word for word in A if not any(bad in word for bad in B)]
... 
>>> words = [''.join([random.choice(string.letters) for _ in range(random.randint(3, 20))])
...          for _ in range(1000)]
>>> blacklist = [''.join([random.choice(string.letters) for _ in range(random.randint(2, 5))])
...              for _ in range(10)]
>>> timeit.timeit('any_filter(words, blacklist)', 'from __main__ import any_filter, words, blacklist', number=100000)
0.36232495307922363
>>> timeit.timeit('regex_filter(words, blacklist)', "from __main__ import re, regex_filter, words, blacklist; blacklist = re.compile('|'.join([re.escape(word) for word in blacklist]))", number=100000)
0.2499098777770996

上述测试将10个随机的黑名单短词(2-5个字符)与1000个随机词(3-20个字符长)列表进行比较,正则表达式的速度快了约50%。


1
嗯,如果黑名单中早期匹配的可能性很高(或者黑名单非常小),那么any()测试可能会更快。始终在合理模拟实际情况的基础上进行测量! - Martijn Pieters
在我的情况下,黑名单只包含10个或更少的单词,但话虽如此,你提出的解决方案非常优雅。 - precicely
2
@user1182556:用10个词,我的解决方案已经更快了。 :-) - Martijn Pieters

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