【Python入門】zip()関数の完全ガイド|複数リストの同時処理・アンパック・応用までやさしく解説!

python

「2つのリストを同じタイミングで処理したい」
「名前のリストと年齢のリストをペアにして扱いたい」

このような場面で非常に便利なのが、Pythonのzip()関数です。

zip()を使うと、複数のリストやタプルを並列に処理することができ、コードがとても読みやすくなります。

この記事では、zip()の基本的な使い方から、ループ処理、データ変換への応用、注意点まで、初心者の方にもわかりやすく解説します。

スポンサーリンク

zip関数の基本的な仕組み

zipの基本構文

zip()関数は、複数のイテラブル(リスト、タプルなど)を受け取り、対応する位置の要素同士をタプルにまとめます。

# 基本的な使い方
fruits = ["りんご", "バナナ", "オレンジ"]
colors = ["赤", "黄色", "オレンジ色"]

# zipで組み合わせる
combined = zip(fruits, colors)
print(list(combined))

実行結果:

[('りんご', '赤'), ('バナナ', '黄色'), ('オレンジ', 'オレンジ色')]

zipの動作を詳しく見る

names = ["田中", "佐藤", "鈴木"]
ages = [25, 30, 28]
jobs = ["エンジニア", "デザイナー", "営業"]

# zipオブジェクトを作成
zipped_data = zip(names, ages, jobs)
print(f"zipオブジェクト: {zipped_data}")

# リストに変換して内容を確認
result = list(zipped_data)
print(f"リスト化した結果: {result}")

# 各タプルの内容
for i, item in enumerate(result):
    print(f"{i}番目: {item}")

実行結果:

zipオブジェクト: <zip object at 0x...>
リスト化した結果: [('田中', 25, 'エンジニア'), ('佐藤', 30, 'デザイナー'), ('鈴木', 28, '営業')]
0番目: ('田中', 25, 'エンジニア')
1番目: ('佐藤', 30, 'デザイナー')
2番目: ('鈴木', 28, '営業')

従来の方法と比較

# 従来の方法(インデックスを使用)
names = ["田中", "佐藤", "鈴木"]
ages = [25, 30, 28]

print("従来の方法:")
for i in range(len(names)):
    print(f"{names[i]}さんは{ages[i]}歳です")

print("\nzip()を使った方法:")
for name, age in zip(names, ages):
    print(f"{name}さんは{age}歳です")

実行結果:

従来の方法:
田中さんは25歳です
佐藤さんは30歳です
鈴木さんは28歳です

zip()を使った方法:
田中さんは25歳です
佐藤さんは30歳です
鈴木さんは28歳です

for文との組み合わせで同時ループ

2つのリストの同時処理

subjects = ["数学", "英語", "理科"]
scores = [85, 92, 78]

print("テスト結果:")
for subject, score in zip(subjects, scores):
    if score >= 90:
        evaluation = "優秀"
    elif score >= 80:
        evaluation = "良好"
    else:
        evaluation = "要努力"
    
    print(f"  {subject}: {score}点 ({evaluation})")

実行結果:

テスト結果:
  数学: 85点 (良好)
  英語: 92点 (優秀)
  理科: 78点 (要努力)

3つ以上のリストの同時処理

cities = ["東京", "大阪", "名古屋"]
populations = [1400, 880, 230]  # 万人
prefectures = ["東京都", "大阪府", "愛知県"]

print("主要都市の情報:")
for city, pop, pref in zip(cities, populations, prefectures):
    print(f"  {city}({pref}): 人口約{pop}万人")

実行結果:

主要都市の情報:
  東京(東京都): 人口約1400万人
  大阪(大阪府): 人口約880万人
  名古屋(愛知県): 人口約230万人

インデックス付きの処理

products = ["パソコン", "マウス", "キーボード"]
prices = [80000, 2000, 5000]

print("商品一覧:")
for i, (product, price) in enumerate(zip(products, prices), 1):
    print(f"  {i}. {product}: {price:,}円")

実行結果:

商品一覧:
  1. パソコン: 80,000円
  2. マウス: 2,000円
  3. キーボード: 5,000円

要素数が異なる場合の動作

短い方に合わせて切り捨て

zip()は、最も短いリストの長さに合わせて動作します。

long_list = [1, 2, 3, 4, 5]
short_list = ['a', 'b', 'c']

result = list(zip(long_list, short_list))
print(f"元のリスト1: {long_list}")
print(f"元のリスト2: {short_list}")
print(f"zip結果: {result}")
print(f"4と5は切り捨てられました")

実行結果:

元のリスト1: [1, 2, 3, 4, 5]
元のリスト2: ['a', 'b', 'c']
zip結果: [(1, 'a'), (2, 'b'), (3, 'c')]
4と5は切り捨てられました

長さの違いを事前にチェック

def safe_zip(*lists):
    """長さをチェックしてからzipする関数"""
    lengths = [len(lst) for lst in lists]
    
    if len(set(lengths)) > 1:
        print(f"警告: リストの長さが異なります {lengths}")
        print(f"最短の長さ {min(lengths)} に合わせて処理します")
    
    return zip(*lists)

# 使用例
list1 = ["A", "B", "C", "D"]
list2 = [1, 2, 3]

result = list(safe_zip(list1, list2))
print(f"結果: {result}")

実行結果:

警告: リストの長さが異なります [4, 3]
最短の長さ 3 に合わせて処理します
結果: [('A', 1), ('B', 2), ('C', 3)]

itertools.zip_longestで全要素を保持

from itertools import zip_longest

list1 = [1, 2, 3, 4, 5]
list2 = ['a', 'b', 'c']

# 通常のzip
normal_zip = list(zip(list1, list2))
print(f"通常のzip: {normal_zip}")

# zip_longest(不足分はNoneで埋める)
long_zip = list(zip_longest(list1, list2))
print(f"zip_longest: {long_zip}")

# 不足分を指定の値で埋める
filled_zip = list(zip_longest(list1, list2, fillvalue='?'))
print(f"fillvalue指定: {filled_zip}")

実行結果:

通常のzip: [(1, 'a'), (2, 'b'), (3, 'c')]
zip_longest: [(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]
fillvalue指定: [(1, 'a'), (2, 'b'), (3, 'c'), (4, '?'), (5, '?')]

アンパック(zip(*iterables))の活用

基本的なアンパック

アスタリスク*を使うと、タプルのリストを「列ごと」に分解できます。

# ペアのリストを列ごとに分解
pairs = [("田中", 25), ("佐藤", 30), ("鈴木", 28)]

names, ages = zip(*pairs)
print(f"名前: {names}")
print(f"年齢: {ages}")
print(f"名前のタイプ: {type(names)}")

実行結果:

名前: ('田中', '佐藤', '鈴木')
年齢: (25, 30, 28)
名前のタイプ: <class 'tuple'>

zipとアンパックの相互変換

# 元のデータ
fruits = ["りんご", "バナナ", "オレンジ"]
colors = ["赤", "黄色", "オレンジ色"]

print("1. 元のデータ:")
print(f"  fruits: {fruits}")
print(f"  colors: {colors}")

# zipで結合
pairs = list(zip(fruits, colors))
print(f"\n2. zipで結合: {pairs}")

# アンパックで分解
fruits_back, colors_back = zip(*pairs)
print(f"\n3. アンパックで分解:")
print(f"  fruits: {list(fruits_back)}")
print(f"  colors: {list(colors_back)}")

実行結果:

1. 元のデータ:
  fruits: ['りんご', 'バナナ', 'オレンジ']
  colors: ['赤', '黄色', 'オレンジ色']

2. zipで結合: [('りんご', '赤'), ('バナナ', '黄色'), ('オレンジ', 'オレンジ色')]

3. アンパックで分解:
  fruits: ['りんご', 'バナナ', 'オレンジ']
  colors: ['赤', '黄色', 'オレンジ色']

行列の転置

# 2次元リスト(行列)の転置
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print("元の行列:")
for row in matrix:
    print(f"  {row}")

# 転置(行と列を入れ替え)
transposed = list(zip(*matrix))
print(f"\n転置後: {transposed}")

print("\n転置後の行列:")
for row in transposed:
    print(f"  {list(row)}")

実行結果:

元の行列:
  [1, 2, 3]
  [4, 5, 6]
  [7, 8, 9]

転置後: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]

転置後の行列:
  [1, 4, 7]
  [2, 5, 8]
  [3, 6, 9]

実用的な応用例

辞書の作成

# キーと値のリストから辞書を作成
keys = ["name", "age", "job", "city"]
values = ["田中", 25, "エンジニア", "東京"]

# zipとdict()で辞書作成
person = dict(zip(keys, values))
print(f"作成した辞書: {person}")

# 複数人分のデータを辞書のリストに
names = ["田中", "佐藤", "鈴木"]
ages = [25, 30, 28]
jobs = ["エンジニア", "デザイナー", "営業"]

people = []
for name, age, job in zip(names, ages, jobs):
    person_dict = {
        "name": name,
        "age": age,
        "job": job
    }
    people.append(person_dict)

print(f"\n人物リスト:")
for person in people:
    print(f"  {person}")

実行結果:

作成した辞書: {'name': '田中', 'age': 25, 'job': 'エンジニア', 'city': '東京'}

人物リスト:
  {'name': '田中', 'age': 25, 'job': 'エンジニア'}
  {'name': '佐藤', 'age': 30, 'job': 'デザイナー'}
  {'name': '鈴木', 'age': 28, 'job': '営業'}

CSVデータの処理

# CSVのような形式のデータ処理
headers = ["商品名", "価格", "在庫"]
data_rows = [
    ["パソコン", 80000, 5],
    ["マウス", 2000, 20],
    ["キーボード", 5000, 15]
]

print("商品情報:")
for row in data_rows:
    # ヘッダーとデータをペアにして表示
    for header, value in zip(headers, row):
        print(f"  {header}: {value}")
    print()  # 空行

# 辞書形式での処理
products = []
for row in data_rows:
    product = dict(zip(headers, row))
    products.append(product)

print("辞書形式:")
for product in products:
    print(f"  {product}")

実行結果:

商品情報:
  商品名: パソコン
  価格: 80000
  在庫: 5

  商品名: マウス
  価格: 2000
  在庫: 20

  商品名: キーボード
  価格: 5000
  在庫: 15

辞書形式:
  {'商品名': 'パソコン', '価格': 80000, '在庫': 5}
  {'商品名': 'マウス', '価格': 2000, '在庫': 20}
  {'商品名': 'キーボード', '価格': 5000, '在庫': 15}

成績処理システム

# 学生の成績処理
students = ["田中", "佐藤", "鈴木", "高橋"]
math_scores = [85, 92, 78, 96]
english_scores = [88, 85, 95, 89]
science_scores = [82, 89, 91, 93]

print("成績一覧:")
print(f"{'名前':<8} {'数学':<4} {'英語':<4} {'理科':<4} {'平均':<6}")
print("-" * 30)

for name, math, eng, sci in zip(students, math_scores, english_scores, science_scores):
    average = (math + eng + sci) / 3
    print(f"{name:<8} {math:<4} {eng:<4} {sci:<4} {average:<6.1f}")

# 教科別の統計
print(f"\n教科別平均点:")
subjects = ["数学", "英語", "理科"]
score_lists = [math_scores, english_scores, science_scores]

for subject, scores in zip(subjects, score_lists):
    avg = sum(scores) / len(scores)
    print(f"  {subject}: {avg:.1f}点")

実行結果:

成績一覧:
名前       数学   英語   理科   平均    
------------------------------
田中       85   88   82   85.0  
佐藤       92   85   89   88.7  
鈴木       78   95   91   88.0  
高橋       96   89   93   92.7  

教科別平均点:
  数学: 87.8点
  英語: 89.2点
  理科: 88.8点

座標データの処理

# 2次元座標の処理
x_coords = [0, 1, 3, 4, 6]
y_coords = [0, 2, 1, 5, 3]

print("座標リスト:")
for i, (x, y) in enumerate(zip(x_coords, y_coords)):
    distance = (x**2 + y**2)**0.5  # 原点からの距離
    print(f"  点{i+1}: ({x}, {y}) - 原点からの距離: {distance:.2f}")

# 座標を移動させる
move_x, move_y = 2, 3
print(f"\n全ての点を({move_x}, {move_y})だけ移動:")
for old_x, old_y in zip(x_coords, y_coords):
    new_x, new_y = old_x + move_x, old_y + move_y
    print(f"  ({old_x}, {old_y}) → ({new_x}, {new_y})")

実行結果:

座標リスト:
  点1: (0, 0) - 原点からの距離: 0.00
  点2: (1, 2) - 原点からの距離: 2.24
  点3: (3, 1) - 原点からの距離: 3.16
  点4: (4, 5) - 原点からの距離: 6.40
  点5: (6, 3) - 原点からの距離: 6.71

全ての点を(2, 3)だけ移動:
  (0, 0) → (2, 3)
  (1, 2) → (3, 5)
  (3, 1) → (5, 4)
  (4, 5) → (6, 8)
  (6, 3) → (8, 6)

zipの注意点と落とし穴

zipオブジェクトは1回だけ使用可能

zip()はイテレータオブジェクトを返すため、一度使用すると空になります。

numbers = [1, 2, 3]
letters = ['a', 'b', 'c']

# zipオブジェクトを作成
zipped = zip(numbers, letters)
print(f"zipオブジェクト: {zipped}")

# 1回目の使用
print("1回目の使用:")
for num, letter in zipped:
    print(f"  {num}: {letter}")

# 2回目の使用(空になる)
print("\n2回目の使用:")
for num, letter in zipped:
    print(f"  {num}: {letter}")
print("  何も表示されませんでした")

# 解決策:リストに変換して保存
zipped_list = list(zip(numbers, letters))
print(f"\nリスト化: {zipped_list}")
print("これなら何度でも使用できます")

実行結果:

zipオブジェクト: <zip object at 0x...>
1回目の使用:
  1: a
  2: b
  3: c

2回目の使用:
  何も表示されませんでした

リスト化: [(1, 'a'), (2, 'b'), (3, 'c')]
これなら何度でも使用できます

メモリ効率との兼ね合い

# 大量のデータでのメモリ効率
def process_large_data():
    # 仮想的な大量データ
    data1 = range(1000000)  # 100万個の数値
    data2 = range(1000000, 2000000)  # 100万個の数値
    
    # zipオブジェクトは必要な時だけメモリを使用
    print("zipオブジェクトを直接使用(メモリ効率良):")
    count = 0
    for a, b in zip(data1, data2):
        if a + b > 1500000:  # 条件に合う最初の要素だけ処理
            print(f"  最初の条件合致: {a} + {b} = {a + b}")
            break
        count += 1
    print(f"  処理した要素数: {count + 1}")

process_large_data()

空のリストでの動作

# 空のリストを含む場合
list1 = [1, 2, 3]
list2 = []  # 空のリスト
list3 = ['a', 'b', 'c']

result = list(zip(list1, list2, list3))
print(f"空のリストがある場合: {result}")
print("→ 結果も空になります")

# 空かどうかのチェック
lists = [list1, list2, list3]
for i, lst in enumerate(lists):
    if not lst:
        print(f"リスト{i+1}が空です")

実行結果:

空のリストがある場合: []
→ 結果も空になります
リスト2が空です

他の関数との組み合わせ

enumerate()との組み合わせ

fruits = ["りんご", "バナナ", "オレンジ"]
prices = [150, 100, 200]

print("商品番号付きリスト:")
for index, (fruit, price) in enumerate(zip(fruits, prices), 1):
    print(f"  {index}. {fruit}: {price}円")

実行結果:

商品番号付きリスト:
  1. りんご: 150円
  2. バナナ: 100円
  3. オレンジ: 200円

map()との組み合わせ

numbers1 = [1, 2, 3, 4]
numbers2 = [10, 20, 30, 40]

# 2つのリストの要素を足し算
sums = list(map(lambda pair: pair[0] + pair[1], zip(numbers1, numbers2)))
print(f"足し算結果: {sums}")

# よりシンプルな書き方
sums2 = [a + b for a, b in zip(numbers1, numbers2)]
print(f"リスト内包表記: {sums2}")

実行結果:

足し算結果: [11, 22, 33, 44]
リスト内包表記: [11, 22, 33, 44]

まとめ

zip()関数は、Pythonで複数のリストを同時に処理するための強力で便利な機能です。

重要なポイント

  • 複数のイテラブルを並列に処理できる
  • 最も短いリストの長さに合わせて動作
  • zipオブジェクトは1回だけ使用可能
  • アンパック(zip(*iterables))で列ごとの分解が可能

よく使うパターン

用途書き方
同時ループfor a, b in zip(list1, list2):ペア処理
辞書作成dict(zip(keys, values))キーと値の結合
行列転置list(zip(*matrix))行と列の入れ替え
データ分解col1, col2 = zip(*pairs)列ごとの抽出

適用場面

  • 複数のリストの同時処理
  • CSVデータの列ごと処理
  • 座標や成績データの管理
  • 辞書の動的作成
  • 行列やテーブルデータの変換

注意すべき点

  • 長さの異なるリストでは短い方に合わせられる
  • zipオブジェクトは再利用できない
  • 大量データでは必要に応じてリスト化

コメント

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