python - 在 for 循环期间修改可迭代的大小 - 如何确定循环?
问题描述
在 python 文档(我发现)中的两个 地方提到了 for 循环。我确实尝试在cpythonfor
中找到循环的源代码,但无济于事。
这就是我想要理解的内容:我曾假设 for 循环是一种while i <= len(iterable) then loop
or if i <= len(iterable) then loop:
。我不确定情况是否如此,原因如下:
y = [1, 2, 3, 4]
for x in y:
print(y)
print(y.pop(0))
Output:
[1, 2, 3, 4]
1
[2, 3, 4]
2
我知道你不应该在循环遍历它时修改它。我知道。但是,这仍然不是随机结果 - 每次运行此代码时都会发生:2 个循环。如果您pop()
改为运行,您还将获得 2 个循环。
也许更奇怪的是,您似乎可靠地获得len(y)+1//2
了循环(至少使用.pop()
,我没有尝试过太多其他测试):
- 如果
y = [1, 2]
有一个循环 - 如果
y = [1, 2, 3]
有两个循环 - 如果
y = [1, 2, 3, 4]
还有两个循环 - 如果
y = [1, 2, 3, 4, 5]
有三个循环 - 如果
y = [1, 2, 3, 4, 5, 6]
还有三个循环 - 如果
y = [1, 2, 3, 4, 5, 6, 7]
有四个循环
根据 Python 文档:
笔记
当序列被循环修改时有一个微妙之处(这只会发生在可变序列中,例如列表)。内部计数器用于跟踪接下来使用哪个项目,并在每次迭代时递增。当此计数器达到序列的长度时,循环终止。这意味着如果套件从序列中删除当前(或前一个)项目,则将跳过下一个项目(因为它获取已处理的当前项目的索引)。同样,如果套件在当前项目之前插入序列中的项目,则当前项目将在下一次循环中再次被处理。这可能会导致讨厌的错误,可以通过使用整个序列的切片制作临时副本来避免这些错误,例如,
for x in a[:]:
if x < 0: a.remove(x)
任何人都可以解释 Python 在循环通过在循环期间修改的可迭代对象时使用的逻辑吗?iter
和StopIteration
和__getitem__(i)
和是如何IndexError
计算的?不是列表的迭代器呢?最重要的是,这是/在文档中的哪里?
正如@Yang K 建议的那样:
y = [1, 2, 3, 4, 5, 6, 7]
for x in y:
print("y: {}, y.pop(0): {}".format(y, y.pop(0)))
print("x: {}".format(x))
# Output
y: [2, 3, 4, 5, 6, 7], y.pop(0): 1
x: 1
y: [3, 4, 5, 6, 7], y.pop(0): 2
x: 3
y: [4, 5, 6, 7], y.pop(0): 3
x: 5
y: [5, 6, 7], y.pop(0): 4
x: 7
解决方案
循环执行直到 iterable 说它没有更多元素。两个循环之后,iterable 已经遍历了两个元素,并且丢失了两个元素,这意味着它到了它的末尾,循环终止。
您的代码等效于:
y = [1, 2, 3, 4]
i = iter(y)
while True:
try:
x=next(i)
except StopIteration:
break
print(y)
print(y.pop(0))
列表迭代器保存下一个要读取的索引。在第三个循环中,列表是[3, 4]
,next(i)
并且需要读取y[2]
,这是不可能的,所以next
raises StopIteration
,结束循环。
编辑关于您的其他问题:
iter
和StopIteration
和__getitem__(i)
和是如何IndexError
计算的?
前两个如上所述:它定义了for
循环。或者,如果你愿意,它是 的合约iter
:它会产生东西,直到它停止StopIteration
。
后两者,我认为根本不参与,因为列表迭代器是用 C 实现的;例如,检查迭代器是否耗尽,直接将当前索引与 进行比较PyList_GET_SIZE
,直接查看->ob_size
字段;它不再通过 Python。显然,您可以创建一个完全使用纯 Python 的列表迭代器,并且您可能会使用它len
来执行检查,或者捕获IndexError
并再次让底层 C 代码对->ob_size
.
不是列表的迭代器呢?
您可以将任何对象定义为可迭代的。当您调用时iter(obj)
,它与调用相同obj.__iter__()
。预计这将返回一个迭代器,该迭代器知道如何处理i.__next__()
(这就是next(i)
翻译的内容)。我相信 dicts 通过在其键列表中设置索引来迭代(我认为,尚未检查)。如果您编写代码,您可以创建一个迭代器,它可以做任何您想做的事情。例如:
class AlwaysEmpty:
def __iter__(self):
return self
def __next__(self):
raise StopIteration
for x in AlwaysEmpty():
print("there was something")
可以预见,不会打印任何内容。
最重要的是,这是/在文档中的哪里?
推荐阅读
- node.js - 使用 Express 接收 JSON 数据块并插入 PostgreSQL
- mongodb - 带有 Spring Data 的 MongoDB 如何有条件地更新插入?
- pine-script - Tradingview Pine 脚本 - 如何让自定义交易量指标表现得像内置的 Vol
- oracle-apex - Oracle APEX:从经典报表的链接列调用内联弹出窗口
- java - 如何解决 ListView 音乐文件中的 ResourceNotFoundException
- javascript - 在 ExpansionPanelSummary [React Material-UI] 中定位内容
- visual-studio-code - 语言服务器如何访问与源文件位于同一目录中的文件
- javascript - XmlHttpRequest 不工作,说模块未定义
- c# - 如何使用随机数据 c# 自动填充类?
- swift - ARKit:不在视图中时停止 AVPlayer 音频