关于python:如果列表中存在项目,如何删除它?

How to delete an item in a list if it exists?

我从带有复选框字段的self.response.get("new_tag")selected_tags的表单文本字段中获取new_tag

1
self.response.get_all("selected_tags")

我把它们组合成这样:

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

(f1.striplist是一个删除列表中字符串内部空格的函数。)

但是在tag_list为空(没有输入新标签)但有一些selected_tags的情况下,new_tag_list包含一个空字符串""

例如,从logging.info

1
2
3
new_tag
selected_tags[u'Hello', u'Cool', u'Glam']
new_tag_list[u'', u'Hello', u'Cool', u'Glam']

如何摆脱空字符串?

如果列表中有空字符串:

1
2
3
4
5
>>> s = [u'', u'Hello', u'Cool', u'Glam']
>>> i = s.index("")
>>> del s[i]
>>> s
[u'Hello', u'Cool', u'Glam']

但是如果没有空字符串:

1
2
3
4
5
6
>>> 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"

但这给了:

1
2
3
4
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

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


1)几乎英国风格:

使用in运算符测试存在,然后应用remove方法。

1
if thing in some_list: some_list.remove(thing)

remove方法将仅删除第一次出现的thing,以便删除所有可能使用while而不是if的事件。

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

2)Duck-typed,EAFP风格:

这种拍摄优先问题 - 最后的态度在Python中很常见。如果对象适合,而不是提前测试,只需执行操作并捕获相关的例外:

1
2
3
4
5
6
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!")

当然,上面例子中的第二个除了条款不仅具有可疑的幽默,而且完全没有必要(关键是要为不熟悉这个概念的人说明鸭子打字)。

如果你期望多次出现的事情:

1
2
3
4
5
while True:
    try:
        some_list.remove(thing)
    except ValueError:
        break
  • 对于这个特定的用例有点冗长,但在Python中非常惯用。
  • 这比#1表现更好
  • PEP 463提出了一个较短的语法,用于尝试/除了简单的用法,这在这里很方便,但它没有得到批准。

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

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

同样,如果您期望多次出现的事情:

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

3)功能风格:

大约在1993年,Python获得了lambdareduce()filter()map(),这得益于一个错过了他们并提交工作补丁*的Lisp黑客。您可以使用filter从列表中删除元素:

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

有一个可能对你的情况有用的快捷方式:如果你想过滤掉空项目(事实上bool(item) == False,如None,零,空字符串或其他空集??合),你可以传递None作为第一个论点:

1
cleaned_list = filter(None, some_list)
  • [update]:在Python 2.x中,filter(function, iterable)过去等同于[item for item in iterable if function(item)](如果第一个参数是None,则[item for item in iterable if item]);在Python 3.x中,它现在相当于(item for item in iterable if function(item))。微妙的区别在于过滤器用于返回列表,现在它的工作方式类似于生成器表达式 - 如果您只是在清理列表上进行迭代并丢弃它,这是可以的,但如果您确实需要列表,则必须将< x8>使用list()构造函数调用。
  • *这些Lispy风格的构造在Python中被认为有点陌生。 2005年左右,Guido甚至在谈论删除filter - 以及同伴mapreduce(它们还没有消失,但reduce被移入functools模块,如果你喜欢高阶命令,这值得一看功能)。

4)数学风格:

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

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

生成器表达式由PEP 289在2.4版本中引入。生成器表达式更适用于您不需要(或想要)在内存中创建完整列表的情况 - 例如,当您只想迭代元素之一时一时间如果您只是在列表上进行迭代,则可以将生成器表达式视为惰性求值列表推导:

1
2
for item in (x for x in some_list if x is not thing):
    do_your_thing_with(item)
  • 请参阅GvR的Python历史博客文章。
  • 此语法的灵感来自数学中的set-builder符号。
  • Python 3也设置和理解了解。

笔记

  • 你可能想使用不等式运算符!=而不是is not(区别很重要)
  • 对于暗示列表副本的方法的批评者:与流行的看法相反,生成器表达并不总是比列表理解更有效 - 请在抱怨之前进行描述

  • 1
    2
    3
    4
    try:
        s.remove("")
    except ValueError:
        print"new_tag_list has no empty string"

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


    如果index找不到搜索到的字符串,它会抛出您正在看到的ValueError。或
    捕获ValueError:

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

    或者使用find,在这种情况下返回-1。

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


    添加此答案是为了完整性,尽管它仅在某些条件下可用。

    如果您有非常大的列表,从列表末尾删除可以避免CPython内部必须memmove,以便您可以重新排序列表。它提供了从列表末尾删除的性能增益,因为它不需要memmove删除后的每个项目 - 返回一步(1)。
    对于一次性删除,性能差异可能是可接受的,但如果您有一个大型列表并需要删除许多项目 - 您可能会注意到性能损失。

    虽然不可否认,在这些情况下,进行完整列表搜索也可能是性能瓶颈,除非项目主要位于列表的前面。

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

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

    item不在列表中时,您可能希望避免引发错误。

    1
    2
    3
    4
    5
    6
    7
    8
    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
  • 虽然我使用CPython对其进行了测试,但很可能大多数/所有其他Python实现都使用数组来在内部存储列表。因此,除非他们使用专为有效列表重新调整大小而设计的复杂数据结构,否则它们可能具有相同的性能特征。
  • A simple way to test this, compare the speed difference from removing from the front of the list with removing the last element:

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

    With:

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

    (gives an order of magnitude speed difference where the second example is faster with CPython and PyPy).

  • 在这种情况下,您可能会考虑使用set,特别是如果列表不是为了存储重复项。实际上,您可能需要存储无法添加到set的可变数据。如果可以订购数据,还要检查btree。

  • Eek,不要做任何复杂的事情:)

    只需filter()你的标签。 bool()为空字符串返回False,而不是

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

    你应该写

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

    或者更好的是,将此逻辑放在striplist()中,以便它首先不返回空字符串。


    抛出那里是另一种单线方法:

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

    它不会创建列表副本,不会通过列表进行多次传递,不需要额外的异常处理,并返回匹配的对象,如果没有匹配则返回None。唯一的问题是它是一个冗长的陈述。

    通常,在寻找不会抛出异常的单行解决方案时,next()是要走的路,因为它是支持默认参数的少数Python函数之一。


    你所要做的就是这个

    1
    2
    3
    4
    5
    list = ["a","b","c"]
        try:
            list.remove("a")
        except:
            print("meow")

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

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