如何删除列表中的项目(如果存在)?


258

new_tag从一个表单文本字段self.response.get("new_tag")selected_tags复选框字段

self.response.get_all("selected_tags")

我将它们像这样组合:

tag_string = new_tag
new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

f1.striplist此函数会在列表中的字符串内去除空格。)

但在这种情况下tag_list是空的(没有新的标签进入),但也有一些selected_tagsnew_tag_list包含一个空字符串" "

例如,来自logging.info

new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

我如何摆脱空字符串?

如果列表中有一个空字符串:

>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

但是,如果没有空字符串:

>>> s = [u'Hello', u'Cool', u'Glam']
>>> if s.index(""):
        i = s.index("")
        del s[i]
    else:
        print "new_tag_list has no empty string"

但这给出了:

Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    if new_tag_list.index(""):
        ValueError: list.index(x): x not in list

为什么会发生这种情况,我该如何解决?

Answers:


718

1)几乎是英式风格:

使用in操作员测试是否存在,然后应用该remove方法。

if thing in some_list: some_list.remove(thing)

remove方法将仅删除的第一个匹配项thing,以便删除您可以while代替使用的所有匹配项if

while thing in some_list: some_list.remove(thing)    
  • 足够简单,可能是我的选择。对于小清单(无法抗拒一线)

2)鸭类型的EAFP风格:

这种先问后问的态度在Python中很常见。无需预先测试对象是否合适,只需执行操作并捕获相关的Exception:

try:
    some_list.remove(thing)
except ValueError:
    pass # or scream: thing not in some_list!
except AttributeError:
    call_security("some_list not quacking like a list!")

当然,上面示例中的第二个except子句不仅具有可疑的幽默性,而且是完全没有必要的(重点是为不熟悉该概念的人说明鸭子式的用法)。

如果您希望事物发生多次:

while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • 这个特定用例有点冗长,但在Python中却很惯用。
  • 这比#1表现更好
  • PEP 463为try / except提出了一种较短的语法,用于简单尝试/例外,在这里很方便,但未获批准。

但是,使用contextlib的prevent()contextmanager(在python 3.4中引入),可以将上述代码简化为:

with suppress(ValueError, AttributeError):
    some_list.remove(thing)

同样,如果您希望事物发生多次:

with suppress(ValueError):
    while True:
        some_list.remove(thing)

3)功能风格:

1993年左右,巨蟒有lambdareduce()filter()map(),一个礼貌的Lisp黑客谁错过了他们,并提交工作补丁*。您可以用来filter从列表中删除元素:

is_not_thing = lambda x: x is not thing
cleaned_list = filter(is_not_thing, some_list)

有一个对您的情况有用的快捷方式:如果您想过滤出空项目(实际上bool(item) == FalseNone零,空字符串或其他空集​​合的项目),则可以将None作为第一个参数传递:

cleaned_list = filter(None, some_list)
  • [update]:在Python 2.x中,filter(function, iterable)曾经等价于[item for item in iterable if function(item)](或[item for item in iterable if item]第一个参数为None);在Python 3.x中,它现在等效于(item for item in iterable if function(item))。细微的区别是过滤器用于返回列表,现在它像生成器表达式一样工作-如果仅遍历已清理的列表并将其丢弃,则可以,但是如果您确实需要列表,则必须将filter()调用括起来与list()构造函数。
  • *这些Lispy风格的构造在Python中被认为有点陌生。2005年左右,圭多甚至被谈论下降filter -与同伴一起mapreduce(他们还没有消失,但reduce被转移到functools模块,这是值得一试,如果你喜欢高阶函数)。

4)数学风格:

PEP 202在2.0版中引入列表理解以来,列表理解成为Python中列表处理的首选样式。其背后的理由是,列表推导提供了一种更简洁的方法,可以在当前使用map()filter()和/或嵌套循环的情况下创建列表。

cleaned_list = [ x for x in some_list if x is not thing ]

PEP 289在2.4版中引入了生成器表达式。生成器表达式更适合您不需要(或不想)在内存中创建完整列表的情况,例如您只想一次遍历一个元素的情况。如果仅遍历列表,则可以将生成器表达式视为延迟的求值列表理解:

for item in (x for x in some_list if x is not thing):
    do_your_thing_with(item)

笔记

  1. 您可能要使用不等式运算符!=代替is not区别很重要
  2. 对于暗示要使用列表副本的方法的批评者:与流行的看法相反,生成器表达式并不总是比列表理解更有效-请在抱怨之前进行剖析

3
我是否可以建议省略(2)中的AttributeError处理?分散注意力,其他部分(或同一部分的其他部分)未处理。更糟糕的是,有人可能会复制该代码,而没有意识到他们过度积极地抑制了异常。原始问题假设一个列表,答案也应该如此。
杰森·库姆斯

1
超级全面的答案!很好,可以通过“样式”将其分为不同的部分。谢谢!
halloleo

哪一个最快?
Sheshank S.

12
try:
    s.remove("")
except ValueError:
    print "new_tag_list has no empty string"

请注意,这只会从列表中删除空字符串的一个实例(就像您的代码一样)。您的列表可以包含多个吗?


5

如果index找不到搜索到的字符串,则会抛出ValueError您所看到的字符串。要么捕获ValueError:

try:
    i = s.index("")
    del s[i]
except ValueError:
    print "new_tag_list has no empty string"

use find,在这种情况下返回-1。

i = s.find("")
if i >= 0:
    del s[i]
else:
    print "new_tag_list has no empty string"

find()是列表属性吗?我正在:>>> s [u'Hello', u'Cool', u'Glam'] >>> i = s.find("") Traceback (most recent call last): File "<pyshell#42>", line 1, in <module> i = s.find("") AttributeError: 'list' object has no attribute 'find'
Zeynel

2
时间Pietscker的remove()方法更为直接:它直接显示了代码的意图(实际上并不需要中间索引i)。
埃里克·O·勒比戈特

1
@Zeynel不,应该在每个Python中使用,请参阅docs.python.org/library/string.html#string.find。但正如EOL指出的那样,仅使用remove会更好。
phihag 2011年

4

添加此答案以提高完整性,尽管它仅在某些条件下可用。

如果列表非常大,则从列表末尾删除可避免CPython内部结构必须使用memmove,因为您可以对列表重新排序。从列表的末尾删除它可以提高性能,因为它不需要删除后一个步骤(1)之后的memmove 每个项目。 对于一次性删除,性能差异是可以接受的,但是如果列表很大并且需要删除很多项目,则可能会发现性能下降。

尽管可以肯定的是,在这些情况下,进行完整列表搜索也可能是性能瓶颈,除非项目大多位于列表的最前面。


只要可以重新排序列表,此方法就可以用于更有效的删除。(2)

def remove_unordered(ls, item):
    i = ls.index(item)
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()

item列表中没有时,您可能要避免引发错误。

def remove_unordered_test(ls, item):
    try:
        i = ls.index(item)
    except ValueError:
        return False
    ls[-1], ls[i] = ls[i], ls[-1]
    ls.pop()
    return True

  1. 当我使用CPython进行测试时,它很可能大多数/所有其他Python实现都使用数组在内部存储列表。因此,除非他们使用设计用于有效调整列表大小的复杂数据结构,否则它们可能具有相同的性能特征。

一种简单的测试方法,比较从列表的最前面移除到最后一个元素所带来的速度差异:

python -m timeit 'a = [0] * 100000' 'while a: a.remove(0)'

带有:

python -m timeit 'a = [0] * 100000' 'while a: a.pop()'

(给出一个数量级的速度差,其中第二个示例使用CPython和PyPy更快)。

  1. 在这种情况下,您可以考虑使用set,尤其是在列表不打算存储重复项的情况下。
    在实践中,尽管您可能需要存储无法添加到的可变数据set。同时检查btree是否可以订购数据。

3

哎呀,不要做任何复杂的事情:)

只是filter()您的标签。 bool()返回False空字符串,所以代替

new_tag_list = f1.striplist(tag_string.split(",") + selected_tags)

你应该写

new_tag_list = filter(bool, f1.striplist(tag_string.split(",") + selected_tags))

或更好的方法是,将此逻辑放入内部,striplist()以使其首先不返回空字符串。


谢谢!所有好的答案,但我想我会用这个。这是我的striplist功能,如何合并您的解决方案:def striplist(l):“”“从列表l中的字符串中去除空格”“”“ return([[x.strip()for l in x]])
Zeynel

1
@Zeynel:当然。你既可以把你的列表理解这样的内部测试:[x.strip() for x in l if x.strip()]或使用Python的内置mapfilter功能是这样的:filter(bool, map(str.strip, l))。如果要进行测试,请在交互式解释器中进行评估:filter(bool, map(str.strip, [' a', 'b ', ' c ', '', ' ']))
dfichter 2011年

在这种情况下,Filter具有一个快捷方式(在布尔上下文中评估元素):对于第一个参数使用None代替bool就足够了。
Paulo Scardine 2013年

2

这是扔到那里的另一种方法:

next((some_list.pop(i) for i, l in enumerate(some_list) if l == thing), None)

它不会创建列表副本,不会在列表中进行多次遍历,不需要其他异常处理,并且会返回匹配的对象;如果没有匹配项,则返回None。唯一的问题是它需要长篇大论。

通常,当寻找一种不会引发异常的单线解决方案时,next()是必经之路,因为它是少数支持默认参数的Python函数之一。


1

您要做的就是这个

list = ["a", "b", "c"]
    try:
        list.remove("a")
    except:
        print("meow")

但是这种方法有问题。你必须把东西放在除了那个地方,所以我发现了这一点:

list = ["a", "b", "c"]
if "a" in str(list):
    list.remove("a")

3
您不应该覆盖内置列表。并且在第二个片段中不需要转换为字符串。
罗伯特·卡斯帕里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.