当我们正在循环的迭代在每次迭代中被修改时,循环的行为是啥[重复]
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中创建连续循环,其中循环的每次迭代在其内部循环的每次迭代中发生一次