2.13. ループの制御

2つのキーワードがループの動作を変えます:

  • 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")

break が発火すると、else ブロックはスキップされます。この構文は「for X in Y: ...; もし一度も抜けなかったなら Z を行う」と読みます。

2.13.4. よくあるループのパターン

実際のスクリプトでは、いくつかのパターンが頻繁に登場します:

  • ポーリング -- ある条件が真になるまで待ってから先に進みます。本体には pass を使います。これは Python の何もしない文で、すべてのインデントされたブロックには少なくとも1つの文が含まれていなければならず、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)
    

    多くの累積ループには、組み込み関数を使った1行の等価な書き方があります。当てはまる場合は組み込み関数を使いましょう:

    • sum() -- 反復可能オブジェクト内のすべての項目を加算します。

    • max() / min() -- 最大または最小の項目を返します。

    • any() -- 少なくとも1つの項目が真であれば 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
    

    組み込み版の方が短く、分かりやすく、同じループを手書きするよりたいてい高速です。