【问题标题】:How to delete an item in a list if it exists?如果存在,如何删除列表中的项目?
【发布时间】:2011-06-22 09:40:00
【问题描述】:

我从带有self.response.get("new_tag") 的表单文本字段中获得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

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

【问题讨论】:

    标签: python list


    【解决方案1】:

    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) Duck-typed, EAFP 风格:

    这种先发后问的态度在 Python 中很常见。与其提前测试对象是否合适,只需执行操作并捕获相关异常即可:

    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's suppress() contextmanager(在python 3.4中引入),上面的代码可以简化为:

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

    再一次,如果您预计事物会多次出现:

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

    3) 功能风格:

    大约在 1993 年,Python 得到了 lambdareduce()filter()map(),感谢 Lisp 黑客错过了它们并提交了工作补丁*。您可以使用filter 从列表中删除元素:

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

    有一个快捷方式可能对您的情况有用:如果您想过滤掉空项目(实际上是 bool(item) == False 的项目,如 None、零、空字符串或其他空集​​合),您可以通过没有作为第一个参数:

    cleaned_list = filter(None, some_list)
    
    • [更新]:在 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))。细微的区别是过滤器用于返回一个列表,现在它像一个生成器表达式一样工作 - 如果您只是迭代清理过的列表并丢弃它,这是可以的,但是如果您真的需要一个列表,则必须将 @ 括起来987654365@ 使用 list() 构造函数调用。
    • *这些 Lispy 风格的构造在 Python 中被认为有点陌生。大约在 2005 年,Guido was even talking about dropping filter - 以及同伴mapreduce(他们还没有消失,但reduce 被移入functools 模块,如果你喜欢high order functions,值得一看)。

    4) 数学风格:

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

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

    PEP 289 在 2.4 版中引入了生成器表达式。生成器表达式更适合您并不真正需要(或不希望)在内存中创建完整列表的情况 - 例如当您只想一次迭代一个元素时。如果您只是对列表进行迭代,则可以将生成器表达式视为lazy evaluated 列表推导:

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

    注意事项

    1. 您可能希望使用不等式运算符!= 而不是is not (the difference is important)
    2. 对于暗示列表副本的方法的批评者:与流行的看法相反,生成器表达式并不总是比列表推导式更有效 - 请在抱怨之前先分析

    【讨论】:

    • 我可以建议省略 (2) 中的 AttributeError 处理吗?它会分散注意力,并且不会在其他部分(或同一部分的其他部分)中处理。更糟糕的是,有人可能会复制该代码而没有意识到他们过于积极地抑制异常。最初的问题假设一个列表,答案也应该是。
    • 超全面解答!很高兴将它按“样式”分成不同的部分。谢谢!
    • 哪个最快?
    【解决方案2】:
    try:
        s.remove("")
    except ValueError:
        print "new_tag_list has no empty string"
    

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

    【讨论】:

      【解决方案3】:

      如果index 没有找到搜索到的字符串,它会抛出您所看到的ValueError。任何一个 捕获 ValueError:

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

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

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

      【讨论】:

      • find() 是列表属性吗?我得到:&gt;&gt;&gt; s [u'Hello', u'Cool', u'Glam'] &gt;&gt;&gt; i = s.find("") Traceback (most recent call last): File "&lt;pyshell#42&gt;", line 1, in &lt;module&gt; i = s.find("") AttributeError: 'list' object has no attribute 'find'
      • Time Pietscker 的remove() 方法更直接:它直接显示代码的用途(确实不需要中间索引i)。
      • @Zeynel 不,它应该在每个 Python 中,请参阅 docs.python.org/library/string.html#string.find 。但正如 EOL 指出的那样,简单地使用 remove 会更好。
      【解决方案4】:

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

      如果您有非常大的列表,从列表末尾删除可以避免 CPython 内部必须 memmove,在您可以重新排序列表的情况下。它可以提高从列表末尾删除的性能,因为它不需要 memmove 每一个 项目之后您删除的项目 - 后退一步 (1)支持>.
      对于一次性删除,性能差异可能是可以接受的,但如果您的列表很大并且需要删除许多项目 - 您可能会注意到性能下降。

      尽管不可否认,在这些情况下,进行完整列表搜索也可能成为性能瓶颈,除非项目大多位于列表的前面。

      此方法可用于更有效的删除,
      只要重新排序列表是可以接受的。 (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,尤其是如果列表不打算存储重复项。
        实际上,您可能需要存储无法添加到@987654329 的可变数据@。还要检查 btree 是否可以订购数据。

      【讨论】:

        【解决方案5】:

        哎呀,别做那么复杂的事情了:)

        只需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): """strips whitespaces from strings in a list l""" return([x.strip() for x in l])
        • @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 ', '', ' ']))
        • Filter 对于这种情况有一个快捷方式(在布尔上下文中评估元素):使用None 而不是bool 作为第一个参数就足够了。
        【解决方案6】:

        这是另一种扔在那里的单线方法:

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

        它不会创建列表副本,不会多次遍历列表,不需要额外的异常处理,如果没有匹配则返回匹配的对象或 None。唯一的问题是它的陈述很长。

        一般来说,在寻找不抛出异常的单行解决方案时,next() 是可行的方法,因为它是少数支持默认参数的 Python 函数之一。

        【讨论】:

          【解决方案7】:

          你要做的就是这个

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

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

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

          【讨论】:

          • 你不应该覆盖 list 内置的。并且在第二个 sn-p 中不需要转换为字符串。
          • 为什么有人赞成这个?我真的不想那么消极,但这就像说要过那条河,你必须建造一架飞机,而不是使用你面前的桥。您可以在 except 块中写pass ...您的第二个解决方案会让任何体面的程序员哭泣。它不仅不必要地低效。它也不起作用,具体取决于字符串转换将使用哪些引号。
          【解决方案8】:

          作为一个班轮:

          >>> s = [u'', u'Hello', u'Cool', u'Glam']
          >>> s.remove('') if '' in s else None # Does nothing if '' not in s
          >>> s
          ['Hello', 'Cool', 'Glam']
          >>> 
          

          【讨论】:

            猜你喜欢
            • 2019-08-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-02-24
            • 2021-11-03
            • 2021-07-19
            • 2016-03-12
            相关资源
            最近更新 更多