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. 常見的迴圈模式

在實際的指令碼中,有幾種模式經常出現:

  • 輪詢(Polling) -- 在繼續往下之前,等待某個條件變為真。主體使用 pass,這是 Python 的無操作陳述式;每個縮排區塊都必須至少包含一條陳述式,而 pass 就是用來表達「這裡什麼都不做」:

    while not ready():
        pass
    proceed()
    
  • 狀態機(State machine) -- 一個 while True: 迴圈,根據狀態變數來決定要做什麼。當工作能清楚拆分成幾個具名階段時很有用:

    state = "header"
    for line in lines:
        if state == "header":
            if line.startswith("---"):
                state = "body"
        elif state == "body":
            print(line)
    
  • 累積(Accumulating) -- 在走訪一個序列的同時逐步建立結果:

    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
    

    內建版本比起手動撰寫相同的迴圈更簡短、更清楚,而且通常更快。