閉じる

Pythonのzip()で複数リストを同時ループする基本と実践

複数のリストを同じ順序で見比べながら処理したい場面は、実務でも学習でも非常によくあります。

Pythonには、そのための標準機能としてzip()が用意されています。

本記事では、zip()の基本から落とし穴の回避、実践的な応用までを段階的に丁寧に解説します。

Pythonのzip()とは何か

複数リストを同時ループする目的

2つ以上のシーケンス(例: リスト、タプル、文字列)において、同じ位置の要素同士をまとめて扱うと理解しやすく、コードも簡潔になります。

例えば、namesscoresを同時に取り出せれば、成績表の出力や、複数列のデータ加工を自然な形で書けます。

基本文法 zip(a, b)

zip(a, b)は、各イテラブルの同じインデックスにある要素を1組のタプルにまとめて順に返します。

最短のイテラブルの長さに合わせて止まるのが特徴です。

Python
# 基本のzip
a = [1, 2, 3]
b = ["x", "y", "z"]

paired = zip(a, b)  # ここではイテレータが返る
print(list(paired)) # 確認のためにリストに展開
実行結果
[(1, 'x'), (2, 'y'), (3, 'z')]

戻り値はイテレータ

Python 3のzip()はイテレータを返します。

したがって、一度消費すると再利用できません。

Python
a = [10, 20]
b = [100, 200]

z = zip(a, b)
print(z)           # zipオブジェクト本体の表示
print(list(z))     # ここで中身を消費
print(list(z))     # もう中身は空
実行結果
<zip object at 0x...>
[(10, 100), (20, 200)]
[]

タプルのアンパックで要素を受け取る

zip()が返す各要素はタプルなので、ループ内でアンパックすると読みやすくなります。

Python
names = ["Alice", "Bob", "Carol"]
scores = [85, 92, 78]

for name, score in zip(names, scores):  # タプルのアンパック
    print(f"{name}: {score}")
実行結果
Alice: 85
Bob: 92
Carol: 78

Pythonでの基本の同時ループ

2つのリストを同時ループ

最も基本的なパターンです。

2列を見比べながら処理できます。

Python
products = ["Pen", "Notebook", "Eraser"]
prices = [120, 280, 80]

for product, price in zip(products, prices):
    print(f"{product} は {price} 円です")
実行結果
Pen は 120 円です
Notebook は 280 円です
Eraser は 80 円です

3つ以上のリストを同時ループ

3列以上でも同様に列挙できます。

使い方は2つのときと同じです。

Python
names   = ["Alice", "Bob", "Carol"]
scores  = [85, 92, 78]
grades  = ["B", "A", "C"]

for name, score, grade in zip(names, scores, grades):
    print(f"{name}: {score} 点 (評価 {grade})")
実行結果
Alice: 85 点 (評価 B)
Bob: 92 点 (評価 A)
Carol: 78 点 (評価 C)

enumerateとzipでインデックスも扱う

インデックスを併せて使うなら、enumerate(zip(...), start=1)が簡潔です。

Python
names  = ["Alice", "Bob", "Carol"]
scores = [85, 92, 78]

for idx, (name, score) in enumerate(zip(names, scores), start=1):
    print(f"{idx}行目 -> {name}: {score}")
実行結果
1行目 -> Alice: 85
2行目 -> Bob: 92
3行目 -> Carol: 78

listで展開して挙動を確認

zip()は最短のイテラブルに合わせて止まります。

list()で展開すると動作確認が容易です。

Python
print(list(zip(range(5), "abc")))  # 文字列"abc"の長さに合わせて止まる
実行結果
[(0, 'a'), (1, 'b'), (2, 'c')]

実践テクニックと応用パターン

長さが異なるリストを安全に処理

「気づかないうちに余りが落ちる」事故を避けるには、2つの方法があります。

  • 厳密に長さ一致を要求する: zip(..., strict=True)を使う(Python 3.10+)
  • 足りない要素を埋めながら処理する: itertools.zip_longest()を使う

まずはstrict=Trueです。

長さが異なると例外を投げ、バグを早期に発見できます。

Python
a = [1, 2, 3]
b = ["x", "y"]

try:
    for x, y in zip(a, b, strict=True):  # 長さが異なるとValueError
        print(x, y)
except ValueError as e:
    print("エラー:", e)
実行結果
エラー: zip() argument 2 is shorter than argument 1

itertools.zip_longestの使いどころ

異なる長さの列を「欠損埋め」で最後まで処理したい場合に有効です。

fillvalueで埋める値を指定できます。

Python
from itertools import zip_longest

left  = ["A", "B", "C", "D"]
right = [10, 20]

for l, r in zip_longest(left, right, fillvalue=None):
    print(f"{l} -> {r}")
実行結果
A -> 10
B -> 20
C -> None
D -> None

Noneではなく空文字や0で埋めたい場合はfillvalue=""fillvalue=0を選びます。

zip(*iterables)で転置

zip()は「列と行の入れ替え(転置)」にも使えます。

*でアンパックして渡すのがポイントです。

Python
# 2x3の「行列」を転置して3x2にする
matrix = [
    [1, 2, 3],
    [4, 5, 6],
]

transposed = list(zip(*matrix))  # 各列がタプルで得られる
print(transposed)
実行結果
[(1, 4), (2, 5), (3, 6)]

また、ペアの「逆zip(アンジップ)」も可能です。

Python
pairs = [("a", 1), ("b", 2), ("c", 3)]
keys, values = zip(*pairs)  # 2つのシーケンスに分解
print(list(keys), list(values))
実行結果
['a', 'b', 'c'] [1, 2, 3]

dict(zip(keys, values))でマッピングを作成

zip()は辞書作成と相性が良く、キー列と値列から辞書を組み立てられます。

Python
keys   = ["id", "name", "score"]
values = [101, "Alice", 92]

record = dict(zip(keys, values))
print(record)
実行結果
{'id': 101, 'name': 'Alice', 'score': 92}

同じキーが重複していると、後勝ちになります。

Python
keys   = ["a", "a", "b"]
values = [1, 2, 3]
print(dict(zip(keys, values)))  # 'a'は最後の2が採用される
実行結果
{'a': 2, 'b': 3}

参考として、長さの違いに関する代表的な選択肢を比較しておきます。

手法挙動(長さが違う場合)用途の目安
zip(a, b)短い方に合わせて静かに切り捨て余りを無視してよいとき
zip(a, b, strict=True)ValueErrorで即時に気づける長さ不一致はバグとみなしたいとき
itertools.zip_longest(…)欠損をfillvalueで埋めて最後まで処理足りない側を埋めつつ全件処理したいとき

初心者が知っておくべき注意点

余った要素が無視される

zip()は最短のシーケンスに合わせて止まるため、余りは静かに捨てられます。

想定外のデータ欠落に注意しましょう。

Python
a = [1, 2, 3, 4]
b = ["x", "y"]

print(list(zip(a, b)))  # 3と4に対応する要素はなく、結果に含まれない
実行結果
[(1, 'x'), (2, 'y')]

zipは一度きりのイテレータ

消費すると再利用できません。

複数回使う必要があるなら、list()で一度展開して使い回すか、再度zip()を作り直します。

Python
a = [1, 2]
b = [10, 20]
z = zip(a, b)

print(list(z))  # ここで消費
# 再利用したいなら、もう一度zipを作る
z = zip(a, b)
print(list(z))
実行結果
[(1, 10), (2, 20)]
[(1, 10), (2, 20)]

変数名と順序で可読性を上げる

アンパック時の変数名は「何が入るか」を端的に表すと読みやすくなります。

また、zip()に渡す引数の順序とアンパックの順序を一致させると誤りを防げます。

Python
# 悪い例: 意味が曖昧
for a, b in zip(names, scores):
    ...

# 良い例: 役割が明確
for name, score in zip(names, scores):
    ...

過剰な入れ子を避ける

zip()を重ねすぎると可読性が落ちます。

中間変数で段階を分けると理解しやすくなります。

Python
# 過剰な入れ子は避ける
# for (n1, s1), (n2, s2) in zip(zip(names1, scores1), zip(names2, scores2)):

# 中間結果を分けて読みやすく
pairs1 = zip(names1, scores1)
pairs2 = zip(names2, scores2)
for (name1, score1), (name2, score2) in zip(pairs1, pairs2):
    print(name1, score1, "vs", name2, score2)
実行結果
# names1やnames2等に応じた出力が行われます(ここでは省略)

まとめ

zip()は、複数のシーケンスを同時に扱うための基本かつ強力な道具です。

タプルのアンパックと組み合わせることで、直感的でPythonicなループが書けます。

長さが異なる場合の扱いは要点で、切り捨てで良いのか、厳密チェック(strict=True)すべきか、欠損埋め(itertools.zip_longest)で最後まで処理するのかを状況に応じて選びましょう。

さらに、転置(zip(*iterables))や辞書生成(dict(zip(...)))などの応用も覚えると、データ処理の幅がぐっと広がります。

まずは基本の2列ループから始め、少しずつ実践的なテクニックを取り入れていくと良いです。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!