从列表中删除以特定字符开头的元素

remove elements from a list that begin with a specific character

我正在尝试从字符串中删除所有提及任何人的内容,我想知道是否有更快的方法来执行此操作?

text = "hey @foo say hi to @bar"
textsplit = text.split()
n = -1
ts2 = textsplit
for x in textsplit:
    n += 1
    if x[0]== "@":
        del ts2[n]
text = ' '.join(ts2)

提前致谢。 (这有点像 Removing elements from a list containing specific characters 但这个有点不同。)

这个呢,使用 re 模块和正则表达式:

print(" ".join(re.sub('^@\w+', '', w) for w in text.split()))

这与您的代码相同:

' '.join(x for x in text.split() if not x.startswith('@'))

这个更简单也更快:

text = "hey @foo say hi to @bar"
newtext = ' '.join([i for i in text.split() if not i.startswith('@')])

我遵从@elyase 和@chris-johnson 对您应该使用的实际简单漂亮代码的回答。

@elyase 的回答更简单,但我认为由于 join 的工作方式,@chris-johnson 的回答可能会稍微高效一些。 @elyase 的代码创建了一个生成器对象,然后 join 将其转换为 运行 之前的列表,我认为这比仅仅创建一个列表有更多的开销。但这是一个小的优化点。

我刚刚在您的示例代码中发现了一些代码味道,所以想指出它们。

text = "hey @foo say hi to @bar"
textsplit = text.split()
n = -1
ts2 = textsplit # code smell 1
for x in textsplit:
    n += 1 # code smell 2
    if x[0]== "@":
        del ts2[n] # code smell 3
text = ' '.join(ts2)

代码味道 1:我想你想用 ts2 = textsplit 创建一个列表的副本,但这并没有发生。您只是为 textsplit 引用的列表创建另一个名称,因此更改 ts2 将更改 textsplit,反之亦然。您可以 ts2 = textsplit[:] 复制非嵌套列表。

代码异味 2:您正在创建一个变量 n 并通过在每次迭代时手动递增来将其用作索引。如果这就是您要做的全部,请改用 for n, x in enumerate(textsplit)

代码味道 3:这里有两件事:

  1. 因为您没有复制 textsplit,所以您正在遍历一个列表并立即修改它 - 无论如何都要避免这种情况,它会导致异常难以推理的错误。
  2. 即使 ts2 是副本,这一行也是有问题的,因为当您删除 ts2 中的元素时,索引会变得不同步。在您的示例中,在删除 '@foo' 之后,索引现在减一,因此尝试使用 ts2[n] access/delete '@bar' 将引发 IndexError。如果你要搞索引twiddling,你需要每删除一个项目就递减n

但一般来说,索引调整是许多错误的来源。如果没有必要,请不要这样做。在 Python 中,您通常不必这样做。

text = "hey @foo say hi to @bar"
newtext = re.sub(' @[!\w]+', '', text)

无需使用任何循环,只需使用正则表达式即可。

我突然想到,所有其他答案都假设您希望删除 @... 子字符串并在不同单词(或其他字符集)之间保持 ' ' 的分隔比 ' '),正如您的代码所证明的那样。但是,问题没有明确指出这是 objective。而且,由于可能存在(不要问我)这种行为不正确的情况,我们开始吧!

编辑: 现在可读且灵活(与旧代码版本相比)

我原来的 post 有点傻,因为代码真的不是为生产而设计的;它起作用了,但仅此而已。这现在毫不费力地完成了三种类型的子字符串减法,尽管也许它可以用正则表达式做得更好(在那里没有太多经验)。

text = "hey @foo say hi to @bar"

普通版只有一个' '来分隔剩余的单词

newText = ''.join(
    text[i] if text.rfind('@', 0, i+2) <= text.rfind(' ', 0, i+1) else
    '' for i in xrange(len(text)))

>>> 'hey say hi to'

仅删除指定的子字符串(不删除任何其他空格)

newText = ''.join(
    text[i] if text.rfind('@', 0, i+1) <= text.rfind(' ', 0, i+1) else
    '' for i in xrange(len(text)))

>>> 'hey  say hi to '

将子字符串转换为空格

newText = ''.join(
    text[i] if text.rfind('@', 0, i+1) <= text.rfind(' ', 0, i+1) else
    ' ' for i in xrange(len(text)))

>>> 'hey      say hi to     '

希望这能有所帮助!