跳到主要内容

循环与迭代

for 循环从可迭代对象中逐个取值并处理;列表、打开的文件等能为遍历提供值的对象都属于可迭代对象。第一个例子中,enumerate(values) 每次提供一个索引和对应的值。第二个例子中,zip 将两组输入逐项配对;strict=True 会在发现长度不一致时报错,而不是在较短的输入耗尽时直接结束。processcompare 代表你要对每个元素或每对元素执行的操作。

for index, value in enumerate(values):
process(index, value)

for left, right in zip(left_values, right_values, strict=True):
compare(left, right)

只有当迭代逻辑本身依赖于数字序列时,才使用 range(start, stop, step)。不要仅仅为了模拟索引操作而滥用它。

While 循环

while 循环在条件为真时持续执行。务必让初始化、进度更新和终止条件在代码中清晰可见:

attempts = 0
while attempts < maximum_attempts:
if try_operation():
break
attempts += 1
else:
raise RuntimeError("operation never succeeded")

循环后的 else 块仅在循环自然结束(未触发 break)时执行。这在处理“搜索耗尽”场景时很有用,但有时提取一个包含提前 return 的辅助函数会更清晰。

控制语句

  • break:立即退出当前最内层循环。
  • continue:跳过当前迭代,直接进入下一轮。
  • pass:空操作,仅作为语法占位符。

避免编写深层嵌套的 break 逻辑。如果需要同时终止多个循环,建议提取函数或维护显式的状态变量。

迭代期间修改容器

在遍历过程中改变容器的大小或结构,会导致跳过元素、重复处理或抛出异常。推荐做法包括:遍历副本、构建新集合,或将“发现”与“修改”步骤分离:

active = [item for item in items if item.enabled]

迭代器是一次性的

大多数迭代器只能遍历一次。将其转换为 list 允许重复遍历,但会将所有数据加载到内存中。如果输入数据量可能很大,请保持流式处理以避免内存溢出。

边界、耗尽与配对输入

迭代教程中的 range 使用左闭右开的区间:不包含 stop,默认从零开始、步长为一;步长为零会抛出 ValueError。步长方向背离 stop 时结果为空。range 保存边界信息,不会预先存下所有整数。

assert list(range(3)) == [0, 1, 2]
assert list(range(5, 0, -2)) == [5, 3, 1]
assert list(range(0, 5, -1)) == []
values = [10, 20]
it = iter(values)
assert next(it) == 10
assert list(it) == [20]
assert list(it) == []
assert list(values) == [10, 20]
assert list(enumerate(values, start=1)) == [(1, 10), (2, 20)]

可迭代对象通过 iter 提供迭代器,迭代器通过 next 依次提供值,直到抛出 StopIteration。列表每次遍历都能提供新迭代器;已耗尽的迭代器则保持耗尽状态。for 会替你处理 StopIteration。没有 strict=True 时,zip 在最短输入耗尽时停止;开启后,长度不同会在消费过程中抛出 ValueError,此时可能已经处理了部分配对。

输入为空或 while 的初始条件为假,也会执行循环的 else。如果 return 或异常使执行离开循环,else 不会执行。while 中的 continue 会重新检查条件,因此跳过进度更新可能造成无限循环。

参考

探索关联打开关联网络