2.13. 循环控制

有两个关键字可以改变循环的运行方式:

  • break —— 立即退出循环。

  • continue —— 跳过当前迭代的剩余部分,开始下一次迭代。

二者都作用于它们所在的最内层循环。

2.13.1. break

使用 break 在某个条件满足时立刻停止循环。这是经典的“查找并停止”模式:

found = None
for item in items:
    if matches(item):
        found = item
        break

if found is not None:
    print("found:", found)

while True: 循环中,break 就是结束循环的方式:

while True:
    line = next_line()
    if line == "quit":
        break
    process(line)

2.13.2. continue

使用 continue 跳过循环体的剩余部分,直接进入下一次迭代。这对于在循环内部筛选元素很方便:

for n in numbers:
    if n < 0:
        continue            # skip negatives
    print(n)

同样的效果也可以用一个包裹循环体剩余部分的 if 块来实现。当跳过条件比保留条件更容易事先说清楚时,continue 有时会更清晰。

在循环内部,continue 扮演的角色与函数中提前 return 相同:跳过你不处理的情况,让主要工作保持在外层缩进上、平铺展开。这种先筛选再处理的循环是其典型形态:

for item in items:
    if item is None:
        continue
    if not item.is_valid():
        continue
    process(item)

缩进后的 process(item) 那一行才是真正的工作;上面每一个守卫语句都明确说明了哪些元素会被跳过。

2.13.3. 循环上的 else

forwhile 都接受一个可选的 else 块。它会在循环没有触发 break 而正常结束时运行。最常见的用途是一个查找循环,希望在什么都没找到时有一个兜底处理:

for item in items:
    if matches(item):
        print("found:", item)
        break
else:
    print("no match")

如果触发了 breakelse 块就会被跳过。可以把这个结构读作“for X in Y: ...;否则,如果我们从未跳出循环,就执行 Z。”

2.13.4. 常见的循环模式

在实际脚本中,有几种模式经常出现:

  • 轮询 —— 在继续往下走之前,等待某个条件变为真。循环体使用 pass,即 Python 的空操作语句;每个缩进块都必须至少包含一条语句,而 pass 正是表示“这里什么都不做”的方式:

    while not ready():
        pass
    proceed()
    
  • 状态机 —— 一个单独的 while True: 循环,根据状态变量来决定要做什么。当工作能够干净地拆分为几个命名阶段时,它很有用:

    state = "header"
    for line in lines:
        if state == "header":
            if line.startswith("---"):
                state = "body"
        elif state == "body":
            print(line)
    
  • 累加 —— 在遍历一个序列的同时逐步构建结果:

    total = 0
    for x in samples:
        total += x
    mean = total / len(samples)
    

    许多累加循环都有使用内置函数的单行等价写法。当某个内置函数适用时,就直接使用它:

    • sum() —— 将可迭代对象中的每个元素相加。

    • max() / min() —— 最大或最小的元素。

    • any() —— 只要至少有一个元素为真值就返回 True

    • all() —— 只有当每个元素都为真值时才返回 True

    • sorted() —— 一个按顺序排列了这些元素的新列表。

    • len() —— 元素的数量(前提是该可迭代对象知道自己的长度)。

    >>> samples = [3, 1, 4, 1, 5, 9, 2, 6]
    >>> sum(samples)
    31
    >>> sum(samples) / len(samples)        # mean
    3.875
    >>> max(samples)
    9
    >>> min(samples)
    1
    >>> sorted(samples)
    [1, 1, 2, 3, 4, 5, 6, 9]
    >>> any([False, False, True])
    True
    >>> all([True, True, False])
    False
    

    内置版本更简短、更清晰,而且通常比手写同样的循环更快。