Python講座 / for・range・enumerate・zipで繰り返す

for文は反復可能なオブジェクトから要素を一つずつ取り出します。値を直接処理し、位置が必要ならenumerate、対応する複数の列を同時に読むならzipを使うと、処理の意図を表しやすくなります。

要素の反復と数値範囲を使い分ける

Pythonのforは、手作業でリストのインデックスを増やす方法だけではありません。リストや文字列の要素を直接取り出せます。range(start, stop, step)はstopを含まず、負のstepなら逆方向へ進みます。rangeはすべての数値を最初から並べたリストではありません。

for value in range(1, 4):
    print(value)

1、2、3が別々の行に出ます。終端の4も含むと思うと、反復回数の見積もりを間違えます。

反復中のアンパックと消費状態は、タプルとシーケンスのアンパックを理解するで扱う規則とつながります。

名前と金額を番号付きで表示する

for_loop.pyへ全体コードを保存し、仮想環境でpython for_loop.pyを実行します。

names = ["coffee", "lunch", "bus"]
amounts = [3500, 8000, 2500]
for number, (name, amount) in enumerate(zip(names, amounts, strict=True), start=1):
    print(number, name, amount)
print(list(range(5, 0, -2)))

予想結果です。最後のrangeは5、3、1を生成しています。

1 coffee 3500
2 lunch 8000
3 bus 2500
[5, 3, 1]

表示番号と長さの要件を確認する

enumerateはカウンターと要素を返します。start=1は表示用の番号を変える指定であり、元リストのインデックスを1始まりに変えません。例では番号と、zipが返す名前・金額の組を入れ子のアンパックで受け取ります。

通常のzipは最も短い入力が尽きると終了します。長さが等しいことが要件なら、Python 3.10以降で使えるstrict=Trueにより、不一致を黙って切り捨てず検出できます。項目が増えてきたら、平行な複数リストより、辞書のリストなどで一件の関係をまとめる方が明確なこともあります。

境界・空入力・変更の影響を試す

末尾の金額を一つ削ってstrictのエラーを確認し、strict=Trueを外すと2行で終わることを比較します。その後、入力とオプションを元へ戻します。range(5, -1, -2)も終端の-1を含まず、5、3、1です。

反復対象が空なら本体は一度も実行されません。終了後に使う変数は、空の場合に備えてループ前に初期化します。反復中の同じリストから要素を消すと、位置がずれて取りこぼすことがあります。読み取りと変更を分け、変更を後へ回すか、新しい絞り込み結果を作ってください。

enumerate(items, start=1) の最初の表示番号は1ですが、元のリストの先頭インデックスは0のままです。表示番号で元データを参照するより、番号と値をアンパックして値を直接使う方が安全です。zipは通常短い方で止まるため、同じ長さが必要なら検証するかstrict=Trueを使います。

関連する公式リファレンス