Pythonのコレクション型入門:リスト、タプル、辞書、セットを理解しよう

Pythonには複数の要素をまとめて扱う「コレクション型」というデータ構造が標準で用意されています。

コレクション型を理解することで、データの整理や操作がぐっと楽になり、プログラムの可読性や保守性も向上します。

本記事では、初心者向けに以下の4つのコレクション型を丁寧に解説します。

  1. リスト(list)
  2. タプル(tuple)
  3. 辞書(dict)
  4. 集合(set)

さらに、最後に「演習問題」と「解答例」を用意しました。学んだ内容を実際に手を動かして復習しましょう。


リスト(list)

1次元リストの基本

リストとは?

リストは順序付きの可変長シーケンスです。

要素の追加・削除が自由にでき、重複を許します。

fruits = ['apple', 'banana', 'orange']

主な操作

インデックスアクセス
print(fruits[0])   # 'apple'
print(fruits[-1])  # 'orange'
要素の追加・削除
fruits.append('grape')    # 末尾に追加
fruits.insert(1, 'melon') # インデックス1に挿入

fruits.remove('banana')   # 指定要素を削除
fruits.pop()              # 末尾を取り出して削除
スライス
nums = [1, 2, 3, 4, 5]
print(nums[1:4])  # [2, 3, 4]
ソート

Python のソート(Timsort)は安定(stable)なので、キーが同じ要素はもとの順序を保ちます。

リストをソートするには大きく分けてlist.sort() メソッド、組み込み関数 sorted()、カスタムソートの3つの方法があります。

# ①list.sort() → 元のリストを変えたい場合に使用
nums = [5, 2, 9, 1, 5, 6]
nums.sort() # デフォルト:昇順
print(nums)  # [1, 2, 5, 5, 6, 9]

# 降順
nums = [5, 2, 9, 1]
nums.sort(reverse=True) # reverse=True:降順
print(nums)  # [9, 5, 2, 1]

# 大文字小文字を区別せず文字列ソート
words = ["banana", "Apple", "cherry"]
words.sort(key=str.lower) # key=:各要素を比較用の「キー」に変換する関数
print(words)  # ['Apple', 'banana', 'cherry']
# ②sorted() → 元とは別にソート結果が欲しい場合に使用
nums = [3, 1, 4, 1, 5]
new_nums = sorted(nums)
print(nums)      # [3, 1, 4, 1, 5]  元は変わらず
print(new_nums)  # [1, 1, 3, 4, 5]

data = ("delta", "alpha", "charlie", "bravo")
print(sorted(data))                # ['alpha', 'bravo', 'charlie', 'delta']
print(sorted(data, reverse=True))  # ['delta', 'charlie', 'bravo', 'alpha']
# ③カスタムソート例
#===タプルのリストを第2要素でソート=====
pairs = [(1, 'b'), (3, 'a'), (2, 'c')]
# 第2要素(インデックス1)で昇順
pairs.sort(key=lambda x: x[1])
print(pairs)  # [(3, 'a'), (1, 'b'), (2, 'c')]

#===辞書のリストを特定キーでソート=====
students = [
    {"name": "Alice", "score": 85},
    {"name": "Bob",   "score": 92},
    {"name": "Carol", "score": 78},
]
# スコアの昇順
sorted_students = sorted(students, key=lambda d: d["score"])
# 降順にしたい場合は reverse=True を追加

#===比較関数を使いたい場合=====
from functools import cmp_to_key

def compare(a, b):
    # たとえば文字列長の逆順でソート
    return len(b) - len(a)

words = ["a", "abcd", "abc"]
words_sorted = sorted(words, key=cmp_to_key(compare))
print(words_sorted)  # ['abcd', 'abc', 'a']
その他のメソッド
  • len(list):長さを取得
  • list.reverse():順序を逆転

2次元リストの基本

作成方法

2次元リストは「リストのリスト」です。例えば、3行2列の表を表現できます。

# 3行2列の2次元リスト
matrix = [
    [1, 2],
    [3, 4],
    [5, 6],
]

上記のmatrixは以下のようなイメージです。

マトリックス列1列2
行112
行234
行356

要素へのアクセス

最初の[行番号]で行を指定し、その後の[列番号]で列を指定します。

print(matrix[0][1])  # 2(1行目、2列目)
print(matrix[2][0])  # 5(3行目、1列目)

各種操作

全要素のループ
for row in matrix:
    for item in row:
        print(item, end=" ")
    print()
出力
1 2 
3 4 
5 6 
行や列の追加・削除
# 行の追加
matrix.append([7, 8])

# 列の追加(各行の末尾に要素を追加)
for row in matrix:
    row.append(0)

# 行の削除
matrix.pop(1)  # 2行目(インデックス1の行)を削除

# 列の削除(各行の末尾要素を削除)
for row in matrix:
    row.pop()
リスト内包表記を使った初期化
# 4行3列のゼロ埋め2次元リスト
zero_matrix = [[0 for _ in range(3)] for _ in range(4)]

3次元リストの基本

作成方法

3次元リストは「2次元リストのリスト」です。例えば、2つの3行2列の「表」をまとめるイメージです。

# 2つの3×2マトリクスをまとめた3次元リスト
tensor = [
    [
        [1, 2],
        [3, 4],
        [5, 6],
    ],
    [
        [7, 8],
        [9, 10],
        [11, 12],
    ],
]
0番目のマトリックス列1列2
行112
行234
行356
1番目のマトリックス列1列2
行178
行2910
行31112

要素へのアクセス

tensor[マトリクス番号][行番号][列番号] の順に指定します。

print(tensor[0][1][1])  # 4:0番目マトリクスの2行2列目
print(tensor[1][2][0])  # 11:1番目マトリクスの3行1列目

各種操作

全要素のループ
for matrix in tensor:
    for row in matrix:
        for item in row:
            print(item, end=" ")
        print()
    print("--- マトリクス区切り ---")
出力
1 2 
3 4 
5 6 
--- マトリクス区切り ---
7 8 
9 10 
11 12 
初期化例
# 2×3×4 の3次元リストをゼロ初期化
zeros_3d = [[[0 for _ in range(4)] for _ in range(3)] for _ in range(2)]

タプル(tuple)

タプルとは?

タプルはリストに似ていますが「不変(immutable)」です。定義後に要素の変更はできません。

point = (10, 20)

タプルの特徴と使い所

  • 要素の更新・削除ができないので、関数の戻り値や定数データとして使うと安全。
  • リストに比べてメモリ使用量が少しだけ少ない。

基本操作

x, y = point        # アンパック
print(point[0])     # 10
print(len(point))   # 2
# point[0] = 5    # エラー:TypeError

辞書(dict)

辞書とは?

辞書は「キーと値」の組み合わせを管理するマッピング型です。

キーは不変型(数値、文字列、タプルなど)しか使えません。

ハッシュテーブルを内部で使っているため、キーによる高速な検索・追加・削除が可能です。

リテラルによる定義
# 空の辞書
person = {}

# 初期要素を持つ辞書
person = {
    'name': 'Alice',
    'age': 30,
    'city': 'Tokyo'
}
コンストラクタによる生成
# dict() コンストラクタ
d3 = dict()  # 空の辞書
d4 = dict(apple=100, banana=200)  # キーを識別子として書ける場合
d5 = dict([('x', 1), ('y', 2)])  # リストやタプルから生成

主な操作

要素のアクセス
prices = {'apple': 100, 'banana': 200}

# キーで直接アクセス
print(prices['apple'])  # 100

# 存在しないキーにアクセスすると KeyError
# print(prices['orange'])  # KeyError

# get() を使うとデフォルト値を返せる
print(prices.get('orange', 0))  # 0
  • prices[key]:キーがなければ KeyError
  • prices.get(key, default):キーがなければ default を返す(省略時は None)
要素の追加・更新・削除
d = {}

# 追加/更新
d['apple'] = 100     # 新規追加
d['apple'] = 120     # 値の更新

# 複数要素のまとめて更新
d.update({'banana': 200, 'orange': 150})

# 削除
del d['banana']      # キーがなければ KeyError
value = d.pop('orange', None)  # 指定キーを取り出して削除。デフォルト値指定可

# popitem は辞書から任意の (key, value) ペアを一つ取り出して削除(Python 3.7 以降は末尾)
k, v = prices.popitem()
  • d[key] = value:追加または更新
  • d.update({…}):他の辞書やキー・値のイテレータで一括更新
  • del d[key]:キーと値を削除
  • d.pop(key[, default]):キーを削除し、値を返す
キーの存在確認
d = {"apple": 150, "banana": 100, "orange": 120}

# ① キーの存在確認
# → キー検索(高速、O(1) 期待時間)
if "apple" in d:
    print("キー 'apple' が存在します")

if "grape" not in d:
    print("キー 'grape' は存在しません")

# ② 値の存在確認
# → 値のリストをレコード走査するため、O(n) のコスト
if 120 in d.values():
    print("値 120 が存在します")

if 200 not in d.values():
    print("値 200 は存在しません")

# ③ キー/値の存在確認
if ("banana", 100) in d.items():
    print("(banana, 100) のペアが存在します")
キー・値一覧の取得
prices = {"apple": 150, "banana": 110, "orange": 120}

# キー一覧
for key in prices.keys():
    print(key)

# 値一覧
for val in prices.values():
    print(val)

# (キー, 値) ペア一覧
for key, val in prices.items():
    print(key, ":", val)
辞書内包表記

リスト内包表記の辞書版です。

# リストから辞書を作成
fruits = ["apple", "banana", "orange"]
prices = [150, 100, 120]

# zip で組にして内包表記
d = {fruit: price for fruit, price in zip(fruits, prices)}
# -> {"apple": 150, "banana": 100, "orange": 120}

# 条件付き
even_squares = {i: i**2 for i in range(10) if i % 2 == 0}
# -> {0: 0, 2: 4, 4: 16, 6: 36, 8: 64}
ネストした辞書

値にさらに辞書を持つケースです。

students = {
    "Alice": {"math": 90, "eng": 85},
    "Bob":   {"math": 75, "eng": 80},
}

# アクセス
alice_math = students["Alice"]["math"]   # 90

# 例:生徒ごとの数学の点数一覧
math_scores = {name: info["math"] for name, info in students.items()}
新しい辞書を作成dict.fromkeys

指定したキーのシーケンス(イテラブル)から、新しい辞書を一度に作成するクラスメソッドです。

全てのキーに同じ値(デフォルトは None)を割り当てられるので、初期化時にまとめてキーだけ用意したいときに便利です。

# 基本構文
dict.fromkeys(iterable, value=None)
  • iterable:キーとして使いたい要素を持つイテラブル(例:リスト、タプル、集合など)
  • value(省略可):全キーに設定するデフォルト値。省略すると None
  • 戻り値:新しい辞書オブジェクト
# キーだけを指定して、値は None
keys = ['a', 'b', 'c']
d = dict.fromkeys(keys)
print(d)  # {'a': None, 'b': None, 'c': None}

# 全キーに同じ値を指定
keys = ('x', 'y', 'z')
d = dict.fromkeys(keys, 0)
print(d)  # {'x': 0, 'y': 0, 'z': 0}

# ミュータブルなデフォルト値に注意
keys = ['k1', 'k2']
d = dict.fromkeys(keys, [])
print(d)  
# {'k1': [], 'k2': []}
d['k1'].append(100)
print(d)
# {'k1': [100], 'k2': [100]}

上記のように、リストや辞書といったミュータブルオブジェクトをデフォルト値にすると、全てのキーが同じオブジェクトを参照するため、意図しない共有状態になります。

対策としては、dict comprehension (辞書包括表記) を使いましょう。

d = {k: [] for k in keys}
print(d)
# {'k1': [], 'k2': []}
d['k1'].append(100)
print(d)
# {'k1': [100], 'k2': []}
主なメソッド一覧
メソッド説明
get(k[, default])キー k の値または default
keys()キーのビュー
values()値のビュー
items()(キー, 値) ペアのビュー
pop(k[, default])キー k を削除し値を返す(なければ default)
popitem()任意のペアを削除し (k, v) を返す
clear()全削除
update(other)他の辞書・イテラブルで更新
setdefault(k[, default])キー k がなければ default で追加し、値を返す

pythonコピーする編集する# update の例 d = {"a": 1, "b": 2} d.update({"b": 20, "c": 30}) # 結果: {"a": 1, "b": 20, "c": 30}

students = {
    "Alice": {"math": 90, "eng": 85},
    "Bob":   {"math": 75, "eng": 80},
}

# アクセス
alice_math = students["Alice"]["math"]   # 90

# 例:生徒ごとの数学の点数一覧
math_scores = {name: info["math"] for name, info in students.items()}

辞書を使う場面・応用例

カウンタ
from collections import Counter
cnt = Counter(["apple", "banana", "apple"])
# -> Counter({'apple': 2, 'banana': 1})
JSON の入出力

Python の辞書 ↔ JSON は json モジュールで相互変換可能。

import json
s = json.dumps(d)      # dict -> JSON 文字列
d2 = json.loads(s)     # JSON 文字列 -> dict
ソート

Python の辞書 (dict) は 3.7 以降、挿入順序を保持します。

ただし「順序付き」といっても自動でソートされるわけではなく、ソート結果を得たい場合は明示的に sorted() などを使って並べ替え、必要に応じて新しい辞書を作成します。

# キーでソート
# ①昇順ソート(デフォルト:文字列ならアルファベット順、数値なら小→大)
d = {"banana": 100, "apple": 150, "orange": 120}

# ソート済みキーのリスト
sorted_keys = sorted(d)  
# ['apple', 'banana', 'orange']

# ソートされた新しい辞書を作る
d_by_key = {k: d[k] for k in sorted_keys}
# {'apple': 150, 'banana': 100, 'orange': 120}

# ②降順ソート
sorted_keys_desc = sorted(d, reverse=True)
# ['orange', 'banana', 'apple']

d_by_key_desc = {k: d[k] for k in sorted_keys_desc}
# 値でソート
# ① 昇順ソート
# items() を値でソート(lambda で第2要素=値を指定)
sorted_items = sorted(d.items(), key=lambda kv: kv[1])
# [('banana', 100), ('orange', 120), ('apple', 150)]

# 新しい辞書に再構築
d_by_value = dict(sorted_items)
# {'banana': 100, 'orange': 120, 'apple': 150}

# ② 降順ソート
sorted_items_desc = sorted(d.items(), key=lambda kv: kv[1], reverse=True)
d_by_value_desc = dict(sorted_items_desc)

たとえば、値が同じ場合はキーの降順でソートする、など複雑な条件を key= で指定できます。

# 複数キー/値でのソート
d = {"a": 3, "b": 1, "c": 3, "d": 2}

# (値, キー) のタプルでソート:まず値昇順、値が同じならキー昇順
sorted_items = sorted(d.items(), key=lambda kv: (kv[1], kv[0]))
# [('b', 1), ('d', 2), ('a', 3), ('c', 3)]

d_custom = dict(sorted_items)

Python 3.6 以前や「挿入順ではなくソート順で常に保持したい」場合は collections.OrderedDict を使うと、挿入・更新順序をカスタム順序で制御できます。

ただし Python 3.7+ の組み込み dict で挿入順序は保持されるので、使い所は減っています。

from collections import OrderedDict

# 値でソートした順番を保持した OrderedDict
od = OrderedDict(sorted(d.items(), key=lambda kv: kv[1]))

集合(set)

集合とは?

集合は「重複を許さない順序なしコレクション」です。

リストやタプルと違い、同じ要素が二度以上含まれることはありません。

集合は数学の集合論に由来し、要素の重複排除や集合演算(和、積、差など)が簡単に行えます。

colors = {'red', 'green', 'blue'}

特徴

  • 要素の順序を持たない
  • 重複が自動で排除される
  • 要素にはイミュータブル(変更不可)な型しか入れられない(例: 数値、文字列、タプル)

集合の生成方法

リテラル表記
s = {1, 2, 3}
set() コンストラクタ
s_from_list = set([1, 2, 2, 3]) # 重複が除かれる
空の集合を作るには
empty = set() # {} は空の辞書になるため注意

要素の追加・削除

追加:add()
s = {1, 2} s.add(3) # {1, 2, 3}
複数追加: update()
s.update([4, 5], {6, 7}) # {1,2,3,4,5,6,7}
削除
s.remove(2) # 2がなければエラー
s.discard(10) # 10がなくてもエラーにならない
ランダム削除: pop()
x = s.pop() # 要素を1つ取り出して削除、返り値は取り出した要素
全削除
s.clear()

集合演算

集合同士の数学的な演算が利用可能です。

演算メソッド記号
和集合 (union)union() または +s1 + s2
積集合 (intersection)intersection() または &s1 & s2
差集合 (difference)difference() または –s1 – s2
対称差 (symmetric difference)symmetric_difference() または ^s1 ^ s2
s1 = {1,2,3}
s2 = {2,3,4}
print(s1 | s2)  # {1,2,3,4}
print(s1 & s2)  # {2,3}
print(s1 - s2)  # {1}
print(s1 ^ s2)  # {1,4}

メンバーシップ(要素の存在確認)

s = {'a', 'b', 'c'}
print('a' in s)   # True
print('d' not in s) # True

集合内包表記

リスト内包表記と同様に、集合でも内包表記が使えます。

double_even = {x*2 for x in range(5) if x % 2 == 0}
# {0,4,8}

リストや文字列との相互変換

リスト→集合: 重複排除
unique = list(set([1,2,2,3,3]))
文字列→集合: ユニークな文字の集合
chars = set('hello') # {'h','e','l','o'}

実用例

  • ユニークな要素集合を扱いたいとき
  • 重複を除きたいとき
  • 複数のリスト間で重複要素を調べたいとき

enumerateを使用したコレクション操作

「何番目の要素か」を同時に取得したい場合、for 文とカウンタ変数を組み合わせる方法もありますが、よりシンプルに書けるのが enumerate 関数です。

enumerate とは?

enumerate は Python の組み込み関数のひとつで、以下のような特徴があります。

  • イテラブル(リストやタプル、文字列など)を受け取り、「インデックス番号」と「要素」 のタプルを返す
  • 内部でカウンタを用意してくれるため、手動で i += 1 のような処理が不要
  • デフォルトでは 0 から始まるが、任意の開始番号を指定できる
>>> fruits = ["apple", "banana", "cherry"]
>>> for index, fruit in enumerate(fruits):
...     print(index, fruit)
0 apple
1 banana
2 cherry

基本的な使い方

シンタックス(構文)
enumerate(iterable, start=0)
  • iterable:リストやタプル、文字列など
  • start:インデックス番号の開始値(省略時は 0)
例:リストのループで番号付き出力
names = ["Alice", "Bob", "Charlie"]
for idx, name in enumerate(names):
    print(f"{idx}番目の名前は{name}です。")
実行結果
0番目の名前はAliceです。
1番目の名前はBobです。
2番目の名前はCharlieです。

開始カウントの変更

デフォルトの 0 ではなく、1 から数えたい場合は、start=1 を指定します。

for idx, name in enumerate(names, start=1):
    print(f"{idx}番目の名前は{name}です。")
実行結果
1番目の名前はAliceです。
2番目の名前はBobです。
3番目の名前はCharlieです。

応用例

辞書のキーと値を同時にループ

辞書のキーと値を同時に扱うには items() を使いますが、番号も付けたいときは enumerate を組み合わせます。

person = {"name": "太郎", "age": 30, "city": "東京"}
for idx, (key, value) in enumerate(person.items(), start=1):
    print(f"{idx}. {key} → {value}")
実行結果
1. name → 太郎
2. age → 30
3. city → 東京
リスト内包表記との組み合わせ

リスト内包表記でも enumerate を使えます。例えば「要素とその番号を組にしたタプルのリスト」を作る例です。

fruits = ["りんご", "みかん", "ぶどう"]
indexed = [(i, fruit) for i, fruit in enumerate(fruits)]
print(indexed)
# -> [(0, 'りんご'), (1, 'みかん'), (2, 'ぶどう')]

注意点

アンパックの漏れ

for item in enumerate(…) と書くと、ループ変数がタプル ((index, element)) になり、アンパックできません。

for tpl in enumerate(fruits):
    print(tpl)  # (0, 'りんご'), (1, 'みかん'), ...

アンパックするには for idx, fruit in enumerate(…) と書きます。

不要な変数

インデックス番号が不要な場合は、enumerate を使わず通常の for fruit in fruits: を使ったほうが可読性が高いです。

巨大リストへの影響

enumerate 自体はイテレータを返すだけなのでメモリ消費は少ないですが、リストの長さが非常に大きい場合はそもそものリスト自体が問題になります。


まとめ

可変性重複許可順序性主な用途
list可変シーケンス操作、順序管理
tuple不変定数データ、関数の戻り値
dict可変–(キー)–(キー)キーと値のマッピング管理
set可変×重複排除、集合演算

演習問題

  1. リスト操作
    nums = [1, 2, 3, 4, 5] の末尾に 6 を追加し、先頭の要素を削除してください。
  2. タプルのアンパック
    coords = (100, 200, 300) をアンパックして変数 x, y, z に代入し、それぞれを表示してください。
  3. 辞書の更新とループ
    student = {‘name’:’Bob’, ‘score’: 80} にキー grade を追加して値を ‘B’ に設定し、辞書の全キーと値をループで表示してください。
  4. セットの集合演算
    A = {‘apple’, ‘orange’, ‘banana’} と B = {‘banana’, ‘kiwi’} の和集合と差集合を求めて表示してください。
  5. 応用問題
    次の辞書 data = {‘a’:[1,2], ‘b’:(3,4), ‘c’:{5,6}} において、キー ‘a’ のリストに 3 を追加し、キー ‘b’ のタプルをアンパックして合計値を計算し、キー ‘c’ のセットに 7 を追加した後の data を表示してください。

解答例

# 1. リスト操作
nums = [1, 2, 3, 4, 5]
nums.append(6)      # [1,2,3,4,5,6]
nums.pop(0)         # 先頭を削除 → [2,3,4,5,6]
print(nums)

# 2. タプルのアンパック
coords = (100, 200, 300)
x, y, z = coords
print(x, y, z)      # 100 200 300

# 3. 辞書の更新とループ
student = {'name':'Bob', 'score': 80}
student['grade'] = 'B'
for key, value in student.items():
    print(f"{key} → {value}")
# name → Bob
# score → 80
# grade → B

# 4. セットの集合演算
A = {'apple', 'orange', 'banana'}
B = {'banana', 'kiwi'}
print("和集合:", A | B)  # {'apple','orange','banana','kiwi'}
print("差集合:", A - B)  # {'apple','orange'}

# 5. 応用問題
data = {'a':[1,2], 'b':(3,4), 'c':{5,6}}
# 'a'のリストに3を追加
data['a'].append(3)        # [1,2,3]
# 'b'のタプルをアンパックして合計
b1, b2 = data['b']
sum_b = b1 + b2            # 7
print("bの合計:", sum_b)
# 'c'のセットに7を追加
data['c'].add(7)           # {5,6,7}
print(data)

以上で「Pythonのコレクション型入門:リスト、タプル、辞書、セットを理解しよう」の解説を終わります。

各コレクション型の特徴を比較しつつ、実際に手を動かして学習することで、Pythonプログラミングの基礎力が確実に身につきます。

頑張って演習問題にも取り組んでみてください!