【问题标题】:Why is it not safe to modify sequence being iterated on?为什么修改被迭代的序列是不安全的?
【发布时间】:2011-03-21 18:35:57
【问题描述】:

在循环中修改被迭代的序列是不安全的(这只会发生在可变序列类型上,例如列表)。如果您需要修改您正在迭代的列表(例如,复制所选项目),您必须迭代一个副本。切片符号使这变得特别方便:

   >>> for x in a[:]: # make a slice copy of the entire list
   ...    if len(x) > 6: a.insert(0, x)
   ... 
   >>> a
   ['defenestrate', 'cat', 'window', 'defenestrate']

为什么只做for x in a 不安全??

【问题讨论】:

  • 你尝试这样做时得到了什么输出?
  • 我相信以下回答了您的问题。如果您不想复制整个列表,您还可以维护一个可以在迭代后运行的后期操作列表。

标签: python


【解决方案1】:

当您修改正在迭代的集合时,迭代器可能会出现意外行为,例如丢失项目或返回相同的项目两次。

这段代码在我运行时无限循环:

>>> a = [ 'foo', 'bar', 'baz' ]
>>> for x in a:
...    if x == 'bar': a.insert(0, 'oops')

这是因为迭代器使用索引来跟踪它在列表中的位置。在列表的开头添加一个项目会导致再次返回项目“bar”,而不是迭代器前进到下一个项目。

【讨论】:

    【解决方案2】:

    这是许多语言中的常见问题。如果您有一个线性数据结构,并且您正在对其进行迭代,则必须跟踪您在结构中的位置。它可能是当前索引或指针,但它是某种指向“当前位置”的手指。

    如果您在迭代过程中修改列表,则光标可能不正确。

    一个常见的问题是您删除了光标下的项目,一切都向下滑动,循环的下一次迭代增加了光标,并且您无意中跳过了一个项目。

    一些数据结构实现提供了在迭代时删除项目的能力,但大多数都没有。

    【讨论】:

    • for x in a: 中,是否在每次迭代中评估a
    • 不,它被评估一次得到一个迭代器,然后迭代器用于循环。
    • 好的。你说:一个常见的问题是你删除了光标下的项目,一切都向下滑动,循环的下一次迭代增加了光标,你无意中跳过了一个项目。:那是怎么回事如果a 仅被评估一次,迭代器将被告知对a 的任何修改?
    • 重点是:迭代器没有收到有关修改的通知,因此它会遗漏或跳过项目。
    • 把迭代器对象想象成一个指向序列的指针。例如,在迭代列表时,迭代器有两个值:对列表的引用和迭代中的当前索引。每次循环时,索引都会增加,并生成列表中的元素。如果更改列表,则不会通知迭代器。但它有一个对列表的引用,并且列表发生了变化。它没有列表的副本。
    【解决方案3】:

    无需太技术性:

    如果您在 Python 中迭代一个可变序列,并且在迭代过程中序列发生了变化,那么并不总是完全清楚会发生什么。如果您在迭代时在序列中插入一个元素,那么现在可以合理地将什么视为序列中的“下一个”元素?如果删除下一个对象怎么办?

    因此,在更改可变序列时迭代它会导致未指定的行为。任何事情都可能发生,具体取决于列表的具体实施方式。 :-)

    【讨论】:

    • 对“任何东西”有什么限制吗?例如。如果你写了一个 C 扩展,当它在迭代时修改的列表崩溃了,那会被认为是严重损坏吗?
    猜你喜欢
    • 2020-09-13
    • 2012-08-09
    • 2012-06-04
    • 2011-08-25
    • 1970-01-01
    • 2017-03-09
    • 1970-01-01
    • 2021-02-04
    • 2014-03-08
    相关资源
    最近更新 更多