当我们正在循环的迭代在每次迭代中被修改时,循环的行为是啥[重复]

Posted

技术标签:

【中文标题】当我们正在循环的迭代在每次迭代中被修改时,循环的行为是啥[重复]【英文标题】:What is the behavior of a loop when the iterable on which we are looping is modified at each iteration [duplicate]当我们正在循环的迭代在每次迭代中被修改时,循环的行为是什么[重复] 【发布时间】:2022-01-19 09:29:03 【问题描述】:

我想知道弹出每个元素时的循环行为,但这可能适用于对迭代器的任何修改。

让我们想象一下:

l = ["elem1", "elem2", "elem3", "elem4"]
for i, elem in enumerate(l):
    l.pop(i)

这很简单,但我想知道:python 是在每次循环迭代时保留一个未修改的 l 实例还是更新 l ?我可以遍历l.copy(),但在这种情况下,我有一个IndexError

我知道我可以简单地解决在列表中删除 elem 的问题,但在这里我试图理解这种行为。

【问题讨论】:

你尝试的时候发生了什么?你有没有检查 python 做了什么,例如由print(i, elem, l) 在循环内? 这能回答你的问题吗? "Strange result when removing item from a list while iterating over it", "Modifying list while iterating" 标准未指定...换句话说,任何事情都可能发生,只有程序员会受到责备。 是的,我测试了它,但不确定如何解释打印结果。感谢帖子分享,很有帮助。实际上我明白我不应该改变我正在循环的列表。 【参考方案1】:
for i in iterable: 
    # some code with i

(在此上下文中具有足够的精度)等价于

iterator = iter(iterable)
while True:
    try:
        i = next(iterator)            
    except StopIteration:
        break
    # some code with i

你可以看到

i 在每次迭代中重新分配 iterator 只创建一次 iterable 的突变可能会也可能不会导致意外行为,具体取决于 iterable.__iter__ 的实现方式。 __iter__ 是负责创建iterator 的方法。

在列表的情况下,iterator 跟踪整数索引,即从列表中提取下一个元素。当您在迭代期间移除一个项目时,后续元素的索引会更改 -1,但不会通知 iterator

>>> l = ['a', 'b', 'c', 'd']
>>> li = iter(l) # iterator pulls index 0 next
>>> next(li)
'a' # iterator pulled index 0, will pull index 1 next
>>> l.remove('a') # 'b' is now at index 0
>>> l
['b', 'c', 'd']
>>> next(li)
'c'

【讨论】:

【参考方案2】:

你为什么不直接测试一下?

>>> for i, elem in enumerate(l):
...     print(i, l)
...     l.pop(i)
...
0 ['elem1', 'elem2', 'elem3', 'elem4']
'elem1'
1 ['elem2', 'elem3', 'elem4']
'elem3'

在底层它只是调用可迭代对象 (PEP279) 上的 next() 方法。因为列表确实会在每次迭代中发生变化,所以它只会引发 StopIteration 因为在您的情况下索引会达到 2。

【讨论】:

【参考方案3】:

当一切都失败时,阅读the manual!

注意当序列被循环修改时有一个微妙之处(这只会发生在可变序列中,例如列表)。内部计数器用于跟踪接下来使用哪个项目,并在每次迭代时递增。当此计数器达到序列的长度时,循环终止。这意味着如果套件从序列中删除当前(或前一个)项目,则将跳过下一个项目(因为它获取已处理的当前项目的索引)。同样,如果套件在当前项目之前插入序列中的项目,则当前项目将在下一次循环中再次被处理。这可能会导致讨厌的错误,可以通过使用整个序列的一部分制作临时副本来避免这些错误,例如,[...]

我不确定关于“递增”的“计数器”是否严格正确,但要点是:不要修改和迭代!

【讨论】:

以上是关于当我们正在循环的迭代在每次迭代中被修改时,循环的行为是啥[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何遍历地图,修改地图但在每次迭代时恢复?

C# 使用IEnumerable,yield 返回结果,同时使用foreach时,在循环内修改变量的值无效

如何在C中创建连续循环,其中循环的每次迭代在其内部循环的每次迭代中发生一次

C# 使用IEnumerable,yield 返回结果,同时使用foreach时,在循环内修改变量的值无效

如何在每次循环迭代时向用户打印不同的语句?

如何创建一个循环来检查每次迭代时是不是有新的 IBAction 输入?