Python | 文法の基本:zip()

Python
スポンサーリンク

zip() の概要(複数の並びを“横に束ねる”)

zip() は、複数のイテラブル(リスト、タプル、文字列など)を「同じ位置の要素どうし」で束ね、タプルの連続として返す関数です。for ループで「並行処理」を書くときの定番で、手動のインデックス管理をなくし、コードを短く安全にします。返り値は“必要になったら順次取り出される”軽量なイテレータです。

names = ["太郎", "花子", "次郎"]
points = [92, 88, 75]
for name, point in zip(names, points):
    print(name, point)
# 太郎 92 / 花子 88 / 次郎 75
Python

基本の使い方と返り値(ここが重要)

同じ位置の要素どうしをタプル化する

zip(iter1, iter2, …) の形で渡すと、(iter1[i], iter2[i], …) のタプルが順に生成されます。返り値は zip オブジェクト(イテレータ)で、for で回すのが基本です。全体を確認したいときだけ list(…) に展開します。

pair = zip(["A", "B", "C"], [1, 2, 3])
print(list(pair))  # [('A', 1), ('B', 2), ('C', 3)]
Python

長さが違うと「短いほうに揃って」切り詰められる(重要)

zip は最短のイテラブルに合わせて止まります。余った要素は無視されるため、意図せぬデータ欠落が起こり得ます。データの完全対応が必要なら、長さを揃えるか、後述の zip_longest を検討します。

print(list(zip([1, 2, 3, 4], ['A', 'B'])))  # [(1, 'A'), (2, 'B')](3,4は捨てられる)
Python

イテレータとしての性質(消費される・メモリ効率・一度きり)

“必要なときだけ取り出す”から大規模でも軽い

zip は全要素を作って保持しません。ループなどで順次取り出すため、巨大コレクションでも余計なメモリを使わずに並行処理できます。

z = zip(range(1_000_000), range(1_000_000))
# そのまま for で回すのが基本。list(z) は展開コストが大きい
Python

一度展開すると中身は消費される

イテレータは“使い切り”です。検証に list(…) した後、同じ zip を再び回すと空になります。必要ならもう一度 zip を作り直します。

z = zip([1,2,3], ['a','b','c'])
print(list(z))  # 展開
for t in z:     # もう空
    print(t)    # 何も出ない
Python

逆操作と行列の転置(zip(*iterables) を使いこなす)

ペアの列を元の列に“ほどく”(アンジップ)

zip で束ねたものや、ペアの列を元の列に戻すには、アンパック記法を使います。

paired = [('A', 1), ('B', 2), ('C', 3)]
letters, numbers = zip(*paired)
print(letters)  # ('A', 'B', 'C')
print(numbers)  # (1, 2, 3)
Python

行列の転置を簡潔に書ける

二次元リストの列・行を入れ替える転置も同様に書けます。各行の長さは揃えておくのが安全です。

matrix = [
    [1, 2, 3],
    [4, 5, 6],
]
transposed = list(zip(*matrix))
print(transposed)  # [(1, 4), (2, 5), (3, 6)]
Python

実用パターン(辞書化、番号付け、複数列の同時処理)

2列から辞書を作る

キーと値のリストがあるなら、zip と dict で一発生成できます。重複キーや長さ不一致には注意します。

keys = ["id", "name", "score"]
vals = [123, "hanako", 92]
record = dict(zip(keys, vals))
print(record)  # {'id': 123, 'name': 'hanako', 'score': 92}
Python

番号も同時に欲しいときは enumerate と組み合わせる

要素ペアに通し番号を付けるなら、enumerate(zip(…), start=1) が読みやすい書き方です。

names = ["太郎", "花子"]
points = [92, 88]
for idx, (name, point) in enumerate(zip(names, points), start=1):
    print(f"{idx}: {name} {point}")
Python

3列以上もまとめて扱う

引数はいくつでも渡せます。組み合わせが増えると可読性が落ちやすいので、意味のある名前で受け取ります。

users = ["A", "B", "C"]
ages  = [20, 30, 40]
scores= [70, 85, 90]
for user, age, score in zip(users, ages, scores):
    print(user, age, score)
Python

例題で身につける(“あるある”な並行処理)

例題1:CSV風のヘッダと行を対応付ける

headers = ["item", "price", "qty"]
row = ["coffee", 350, 2]
print(dict(zip(headers, row)))  # {'item': 'coffee', 'price': 350, 'qty': 2}
Python

例題2:異なる長さの列は“最短に揃う”ことを確認

names = ["A", "B", "C", "D"]
points = [10, 20]
print(list(zip(names, points)))  # [('A', 10), ('B', 20)]
Python

例題3:アンジップで列ごとに処理を分離

pairs = [("apple", 120), ("banana", 90), ("orange", 150)]
items, prices = zip(*pairs)
print(items)   # ('apple', 'banana', 'orange')
print(sum(prices))  # 360
Python

例題4:行列の転置と整形表示

table = [
    ["name", "price", "qty"],
    ["coffee", 350, 2],
    ["tea", 280, 1],
]
for col in zip(*table):
    print(col)
# ('name', 'coffee', 'tea')
# ('price', 350, 280)
# ('qty', 2, 1)
Python

落とし穴と対策(重要ポイントの深掘り)

長さ不一致の見落としに注意する

zip は“最短で切る”ため、余りが静かに捨てられます。完全対応が必要なら事前に長さを検証するか、余りも扱う仕様なら itertools.zip_longest を検討します(不足側に埋め草を入れられます)。

from itertools import zip_longest
print(list(zip_longest(["A","B","C"], [1], fillvalue=None)))  # [('A',1), ('B',None), ('C',None)]
Python

イテレータは一度きり

検証で list(…) に展開した後は再利用できません。再度使いたい場合は zip を作り直すか、展開せず直接 for で処理します。

可読性を最優先に

引数が多くなったり、ネストした zip を乱用すると意図が読めなくなります。必要なら前処理を変数に分け、意味のある名前でタプルを受け取り、責務を分離して書きます。


まとめ

zip() は「位置対応の要素を束ねて並行処理する」ための基本ツールです。返り値は軽いイテレータで、長さ不一致では“最短に合わせて”切り詰められる点が重要です。アンパックでの逆変換 zip(*iterables) を押さえると、辞書化・転置・集計がスムーズになります。enumerate との組み合わせで番号付けも簡潔に。長さ検証や zip_longest の活用、イテレータの一度きり性質を理解しておけば、初心者でも安全で読みやすい並行処理を書けます。

タイトルとURLをコピーしました