【问题标题】:Why is list.remove only removing every second item?为什么 list.remove 只删除每隔一个项目?
【发布时间】:2013-05-09 15:02:53
【问题描述】:

在我的 Python 2.7.2 IDLE 解释器中:

>>> mylist = [1, 2, 3, 4, 5]
>>> for item in mylist:
        mylist.remove(item)

>>> mylist
[2, 4]

为什么?

【问题讨论】:

  • 在迭代列表时不要更改列表....
  • 顺便说一句,我确实在另一个问题上看到正确的方法是 del mylist[:]

标签: python list enumeration


【解决方案1】:

这是因为当您遍历列表时,python keeps track of the index in the list。请考虑以下代码:

for i in range(len(mylist)):
    if i >= len(mylist):
       break
    item = mylist[i]
    mylist.remove(item)

如果我们跟踪这一点(这实际上是 python 在您的代码中所做的事情),那么我们会看到,当我们删除列表中的一个项目时,右侧的数字向左移动一个位置以填充左侧的空白我们删除了该项目。正确的项目现在位于索引 i 处,因此它实际上不会在迭代中看到,因为接下来发生的事情是我们为 for 循环的下一次迭代增加 i


现在来点聪明的。相反,如果我们向后迭代列表,我们将清除列表:

for item in reversed(mylist):
    mylist.remove(item)

这里的原因是我们在 for 循环的每次迭代中从列表末尾取出一个项目。由于我们总是从末尾删除项目,因此无需更改任何内容(假设列表中的唯一性 -- 如果列表不是唯一的,则结果是相同的,但参数会变得更复杂一些)。

当然,如果您想从列表中删除所有项目,您可以非常轻松地做到这一点:

del mylist[:]

甚至使用切片分配:

mylist[:] = []

(我之所以提到后者,是因为用其他甚至不需要长度相同的项目替换列表的片段可能很有用)。

【讨论】:

  • 我喜欢 Python,但有一些奇怪的问题需要对底层实现/概念有相当深入的了解。
【解决方案2】:

那是因为您在迭代列表时正在修改列表,而是迭代浅表副本:

>>> mylist = [1, 2, 3, 4, 5]
>>> for item in mylist[:]:      #use mylist[:] or list(mylist)
            mylist.remove(item)
...     
>>> mylist
[]

【讨论】:

    【解决方案3】:

    您在循环浏览列表时正在修改您的列表,这是非常糟糕的做法。

    【讨论】:

      【解决方案4】:

      问题是您在迭代列表时正在更改列表。改为使用列表推导:

      mylist = [1, 2, 3, 4, 5]
      mylist = [x for x in mylist if condition(x)]
      

      【讨论】:

      • 这不是未定义的行为。
      • Python 规范没有提到在这种极端情况下将采用什么行为,所以它是一个实现细节,因此未定义。如果我愿意,我可以编写一个 Python 虚拟机,它会在这种情况下爆炸并且它仍然是符合要求的。
      • 规范确实详细说明了它是如何工作的。我不记得在哪里,但有一次我记得搜索它并找到它明确拼写出来。
      • 我不同意。规范中依赖于 Cpython 实现的所有内容都明确指出“Cpython 实现细节......”。这是对MutableSequence.__iter__实现的约束
      【解决方案5】:
      >>> mylist = [1, 2, 3, 4, 5]
      
      >>> for item in mylist:
              mylist.remove(item)
      

      在这个循环中: 第一次它将第 0 个元素 (1) 作为一个项目并将其从 mylist 中删除。所以之后 mylist 将为 [2,3,4,5] 但指针将位于新 mylist 的第 1 个元素中 ([2 ,3,4,5]) 即 3. 它将删除 3.所以 2 不会从列表中删除。

      这就是为什么在完整操作后会留下 [2,4]。

      using for loop:-
      >>>for i in range(len(mylist)):
             mylist.remove(mylist[0])
             i-=1
      
      >>>mylist
      []
      

      你可以使用 while 循环来做到这一点:

      >>>mylist = [1, 2, 3, 4, 5]
      >>>while (len(mylist)>0):
             mylist.remove(mylist[0])
             print mylist
      

      【讨论】:

        【解决方案6】:

        使用mylist的深层副本

        mylist = [1, 2, 3, 4, 5]
        
        l = [k for k in mylist] # deep copy
        
        for i in range(len(mylist)):
            mylist.remove(l[i])
            print 
        
        print mylist
        

        【讨论】:

          猜你喜欢
          • 2021-05-28
          • 2016-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-12-16
          相关资源
          最近更新 更多