【问题标题】:How to remove all elements that doesn't meet requirements from list如何从列表中删除所有不符合要求的元素
【发布时间】:2015-09-03 07:45:55
【问题描述】:

我有一个清单:

my_list = ['a', 'b', 'c', 'a', 'b', 'c', 'a']

我使用以下代码删除不符合要求的元素:

[my_list.remove(element) for element in my_list if 'a' not in element]

但不是预期的['a', 'a', 'a'] 得到了['a', 'c', 'a', 'c', 'a']。似乎在删除'b' 之后,Python 不会检查以下'c' 元素...

请告诉我如何解决此问题并有效地从列表中删除所有不必要的元素。

【问题讨论】:

  • this
  • 您想要就地修改列表还是缺少指定元素的新列表?
  • 是的。我想修改已有列表

标签: python list


【解决方案1】:

其他答案解决了这个问题,但让我解释一下这里发生了什么。

>>> lst = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
>>> for each in lst:
...     if 'a' not in each:
...         lst.remove(each)
>>> lst
['a', 'c', 'a', 'c', 'a']

迭代 1:

#   V                                     - Current position of loop
# ['a', 'b', 'c', 'a', 'b', 'c', 'a']

if 'a' not in each: #Output False

迭代 2:

#        V                                - Current position of loop
# ['a', 'b', 'c', 'a', 'b', 'c', 'a']

if 'a' not in each: #Output True
    list.remove(each)  #Element from position 1 ('b') in list is removed

迭代 3:

#             V                         |___ Supposed to be like this  
# ['a', 'b', 'c', 'a', 'b', 'c', 'a']   |

#             V                         |___ Updated list
# ['a', 'c', 'a', 'b', 'c', 'a']        |

if 'a' not in each: #Output False

这就是为什么你的 'c' 在输出列表中被跳过的原因。

现在要解决您的问题,与其删除所有非a,不如创建一个仅包含a 的列表。 (Trengot's Answer)

编辑:

由于你的my_list是一个字符的集合,最好使用if 'a' != element,因为'a' not in element会扫描元素的每个字母,它也会删除所有带有字母'a'的元素(@987654322 @)。

例如,如果您的 my_list = ['a','abc','fd','b','c']'a' not in 'abc' 将返回 False,并且元素 'abc' 将不会被删除。

【讨论】:

    【解决方案2】:

    将列表过滤到一个新的列表中,选择您想要的元素,而不是删除您不想要的元素。然后要么使用新的,要么分配给旧的。

    my_list = [element for element in my_list if 'a' in element]
    

    正如 Peter Wood 所指出的,这将为my_list 分配一个新对象。如果您想保留相同的列表对象(例如,如果它在其他地方也被引用)将新列表分配给my_list[:]

    my_list[:] = [element for element in my_list if 'a' in element]
    

    【讨论】:

    • 如果要保留原始列表对象,分配给my_list[:]
    【解决方案3】:

    由于您想就地修改(缩小)现有列表,因此可以这样做:

    def remove_all_on_predicate(predicate, list_):
        deserving_removal = [elem for elem in list_ if predicate(elem)]
        for elem in deserving_removal:
            list_.remove(elem)
        return None
    
    >>> remove_all_on_predicate(lambda x: "a" not in x, my_list)
    >>> my_list
    ['a', 'a', 'a']
    

    【讨论】:

      【解决方案4】:

      正如您所发现的,尝试从您正在迭代的列表中删除元素可能不会达到您的预期。 Ashwani Agarwal 的回答说明了它失败的原因,其他答案显示了可用于正确执行删除的各种技术。当您有一个无法复制的非常大的列表时,另一种有用的技术是反向迭代它:

      my_list = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
      for element in reversed(my_list):
          if 'a' not in element:
              my_list.remove(element)
              print(element, my_list)
      
      print('Final:', my_list)
      
      my_list = ['a', 'b', 'c', 'a', 'b', 'c', 'a']
      for element in reversed(my_list):
          if 'a' in element:
              my_list.remove(element)
              print(my_list)
      
      print('Final:', my_list)
      

      输出

      c ['a', 'b', 'a', 'b', 'c', 'a']                                                                                                               
      c ['a', 'b', 'a', 'b', 'a']                                                                                                                    
      b ['a', 'a', 'b', 'a']                                                                                                                         
      b ['a', 'a', 'a']                                                                                                                              
      Final: ['a', 'a', 'a']                                                                                                                         
      ['b', 'c', 'a', 'b', 'c', 'a']                                                                                                                 
      ['b', 'c', 'b', 'c', 'a']
      ['b', 'c', 'b', 'c']
      Final: ['b', 'c', 'b', 'c']
      

      此代码使用reversed() 函数,该函数返回一个迭代器,覆盖您传递给它的可迭代对象;它不会复制可迭代的。

      我应该提到,这种技术比其他答案中给出的过滤方法效率低。这是因为my_list.remove(element) 的每次调用都必须扫描my_list 直到找到匹配的元素,所以它的复杂度为O(n**2),其中n 是列表中元素的数量;过滤算法的复杂度为 O(n)。因此,正如我之前所说,这种方法仅在列表太大以至于您无法负担 RAM 来创建新列表的情况下才有用。

      关于您问题中的代码,我需要提及的另一件事是:当您应该使用普通的 for 循环时,您正在使用列表推导来循环列表。 list.remove() 返回None,因此您的代码不必要地创建了一个充满Nones 的列表,然后将该列表丢弃。一般规则是:不要将列表推导纯粹用于您在其中调用的函数的副作用。

      【讨论】:

      • 太棒了 :) 如果你像 agarwal 那样展示迭代流程会更好
      • @VigneshKalai:谢谢!但我认为我在循环中的print() 调用充分显示了流程。 :)
      • 只是一开始我很困惑你的方法是如何工作的,看到你的打印我发现remove 删除了我只是认为它很容易理解的元素的第一次出现新的 python 程序员。最后但并非最不重要的beautiful answer
      【解决方案5】:

      我会使用filter

      my_list = filter(lambda x: 'a' in x, my_list)
      

      【讨论】:

        猜你喜欢
        • 2016-02-13
        • 1970-01-01
        • 2022-07-06
        • 2022-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多