【问题标题】:Why does Python skip elements when I modify a list while iterating over it?当我在迭代列表时修改列表时,为什么 Python 会跳过元素?
【发布时间】:2010-10-19 01:08:20
【问题描述】:

我目前正在用 python 开发一个程序,我刚刚注意到语言中的 foreach 循环或列表结构有问题。我将给出一个通用示例来简化我的问题,因为我的程序和通用示例都出现了相同的错误行为:

x = [1,2,2,2,2]

for i in x:
    x.remove(i)

print x        

好吧,这里的问题很简单,我认为这段代码应该从列表中删除所有元素。好吧,问题是在它执行之后,我总是在列表中得到 2 个剩余元素。

我做错了什么?感谢您提前提供的所有帮助。

编辑:我不想清空列表,这只是一个例子......

【问题讨论】:

  • 您遇到的问题有以下解决方案——但我不禁认为应该缩短为“x = []”。如果您发布导致问题的真实代码可能会更有趣?
  • 你应该发布你真正的问题。 list.remove 效率很低,因为它必须搜索列表,然后将删除后的所有元素向前移动。

标签: python list foreach


【解决方案1】:

这是 Python 中一个有据可查的行为,您不应该修改正在迭代的列表。试试这个:

for i in x[:]:
    x.remove(i)

[:] 返回x 的“切片”,恰好包含其所有元素,因此实际上是x 的副本。

【讨论】:

  • 这并不能解释为什么在 rogeriopvl 的问题中列表包含两个元素。如果这个解释是真的,那么这个列表应该是完整的
  • @bgbg:“它不安全”完美地解释了这种行为。像这样模糊的规范是一种说“未定义”的方式
  • +1 是解决这个问题的最优雅的方法。向后迭代似乎更加混乱。
  • 当你需要一个列表副本时,list(x) 比 x[:] 更符合 Python 风格
【解决方案2】:

当你删除一个元素,并且 for 循环 incs 到下一个索引时,你会跳过一个元素。

倒着做。或者请说明你真正的问题。

【讨论】:

    【解决方案3】:

    我认为,从广义上讲,当你写作时:

    for x in lst:
        # loop body goes here
    

    在后台,python 正在做这样的事情:

    i = 0
    while i < len(lst):
        x = lst[i]
        # loop body goes here
        i += 1
    

    如果你在循环体中插入lst.remove(x),也许你就能明白为什么会得到这样的结果?

    本质上,python 使用移动指针来遍历列表。指针从指向第一个元素开始。然后删除第一个元素,从而使 second 元素成为新的第一个元素。然后指针移动到新的第二个——之前是第三个——元素。等等。 (如果您使用 [1,2,3,4,5] 而不是 [1,2,2,2,2] 作为示例列表,可能会更清楚)

    【讨论】:

      【解决方案4】:

      你为什么不直接使用:

      x = []
      

      这可能是因为您正在更改您正在迭代的同一个数组。

      如果您想按照自己的方式清除阵列,请尝试 Chris-Jester Young 的回答。

      【讨论】:

        【解决方案5】:

        我知道这是一篇旧帖子,答案已被接受,但对于那些可能仍会出现的人...

        之前的一些答案表明在迭代期间更改可迭代对象是一个坏主意。但作为强调正在发生的事情的一种方式......

        >>> x=[1,2,3,4,5]
        >>> for i in x:
        ...     print i, x.index(i)
        ...     x.remove(i)
        ...     print x
        ...
        1 0
        [2, 3, 4, 5]
        3 1
        [2, 4, 5]
        5 2
        [2, 4]
        

        希望视觉有助于澄清。

        【讨论】:

          【解决方案6】:

          我同意 John Fouhy 关于休息条件的看法。正如 Chris Jester-Young 所建议的,遍历列表的副本适用于 remove() 方法。但是,如果需要 pop() 特定项目,则如 Erik 所述,反向迭代可以工作,在这种情况下,操作可以就地完成。例如:

          def r_enumerate(iterable):
              """enumerator for reverse iteration of an iterable"""
              enum = enumerate(reversed(iterable))
              last = len(iterable)-1
              return ((last - i, x) for i,x in enum)
          
          x = [1,2,3,4,5]
          y = []
          for i,v in r_enumerate(x):
              if v != 3:
                  y.append(x.pop(i))
              print 'i=%d, v=%d, x=%s, y=%s' %(i,v,x,y)
          


          或使用 xrange:

          x = [1,2,3,4,5]
          y = []
          for i in xrange(len(x)-1,-1,-1):
              if x[i] != 3:
                  y.append(x.pop(i))
              print 'i=%d, x=%s, y=%s' %(i,x,y)
          

          【讨论】:

            【解决方案7】:

            如果您需要过滤列表中的内容,使用列表理解可能是一个更好的主意:

            newlist = [x for x in oldlist if x%2]
            

            例如将从整数列表中过滤掉所有偶数

            【讨论】:

              猜你喜欢
              • 2020-09-07
              • 2012-06-04
              • 2018-10-12
              • 2021-04-26
              • 2014-09-17
              • 1970-01-01
              • 2021-07-16
              • 2017-12-05
              相关资源
              最近更新 更多