リスト・タプル・辞書¶
この章で学ぶこと¶
- リスト(list)の作成・アクセス・スライス・主要メソッド
- タプル(tuple)の特徴と使いどころ
- 辞書(dict)のキーと値の操作
- 集合(set)の基本
- リスト内包表記による効率的なリスト生成
- コレクションに関するよくある間違い
コレクション型の比較¶
Python の主要なコレクション型を比較した表です。
| 型 | 表記 | 順序 | 重複 | ミュータブル | 主な用途 |
|---|---|---|---|---|---|
list |
[1, 2, 3] |
あり | 可 | はい | 汎用的な順序付きデータ |
tuple |
(1, 2, 3) |
あり | 可 | いいえ | 変更不可のデータ、関数の複数戻り値 |
dict |
{"a": 1} |
あり* | キーは不可 | はい | キーと値のペア |
set |
{1, 2, 3} |
なし | 不可 | はい | 重複除去、集合演算 |
(*) Python 3.7 以降、辞書は挿入順序を保持します。
リスト(list)¶
リストは、複数の値を順番に格納できるデータ構造です。Python で最もよく使われるコレクションです。
リストの作成¶
# リストの作成
fruits = ["りんご", "みかん", "ぶどう"]
numbers = [1, 2, 3, 4, 5]
empty_list = [] # 空のリスト
# 異なる型を混在させることも可能(ただし通常は同じ型で統一する)
mixed = [1, "hello", 3.14, True]
インデックスによるアクセス¶
リストの各要素にはインデックス(添字)でアクセスします。インデックスは 0 から始まることに注意してください。
fruits = ["りんご", "みかん", "ぶどう", "もも", "いちご"]
print(fruits[0]) # りんご(先頭の要素)
print(fruits[2]) # ぶどう(3 番目の要素)
print(fruits[-1]) # いちご(末尾の要素)
print(fruits[-2]) # もも(末尾から 2 番目)
よくある間違い
範囲外のインデックスにアクセスする:
fruits = ["りんご", "みかん", "ぶどう"]
# 間違い: 存在しないインデックス
# print(fruits[3]) # IndexError: list index out of range
# print(fruits[10]) # IndexError
# 正しい: インデックスは 0 から len(fruits)-1 まで
print(fruits[0]) # りんご
print(fruits[2]) # ぶどう(最後の要素)
print(fruits[-1]) # ぶどう(末尾の要素にアクセスする安全な方法)
リストの長さを確認してからアクセスすると安全です:
要素の変更¶
リストはミュータブル(変更可能)なので、要素を後から変更できます。
スライス¶
スライスを使うと、リストの一部を切り出すことができます。
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numbers[2:5]) # [2, 3, 4](インデックス 2 から 4 まで)
print(numbers[:3]) # [0, 1, 2](先頭から 3 つ)
print(numbers[7:]) # [7, 8, 9](インデックス 7 から末尾まで)
print(numbers[::2]) # [0, 2, 4, 6, 8](2 つおきに取得)
print(numbers[::-1]) # [9, 8, 7, ..., 0](逆順)
スライスの書式
リスト[開始:終了:ステップ] の形式です。終了のインデックスの要素は含まれないことに注意してください。range() と同じ考え方です。
スライスは IndexError にならない
インデックスアクセス(lst[10])は範囲外でエラーになりますが、スライス(lst[10:20])は範囲外でもエラーにならず、空リストや利用可能な範囲の結果を返します。
リストの主要メソッド¶
fruits = ["りんご", "みかん"]
# append: 末尾に要素を追加
fruits.append("ぶどう")
print(fruits) # ['りんご', 'みかん', 'ぶどう']
# insert: 指定位置に要素を挿入
fruits.insert(1, "バナナ")
print(fruits) # ['りんご', 'バナナ', 'みかん', 'ぶどう']
# pop: 指定位置の要素を取り出して削除(デフォルトは末尾)
removed = fruits.pop()
print(removed) # ぶどう
print(fruits) # ['りんご', 'バナナ', 'みかん']
# remove: 値を指定して削除(最初に見つかったもの)
fruits.remove("バナナ")
print(fruits) # ['りんご', 'みかん']
# index: 値のインデックスを取得
print(fruits.index("みかん")) # 1
# len: リストの長さ(要素数)を取得
print(len(fruits)) # 2
# in: 要素が含まれているか確認
print("りんご" in fruits) # True
print("もも" in fruits) # False
よくある間違い
append と extend の混同:
# append: 要素を 1 つ追加する
a = [1, 2, 3]
a.append([4, 5])
print(a) # [1, 2, 3, [4, 5]](リストがそのまま 1 要素として追加される)
# extend: 別のリストの要素をすべて追加する
b = [1, 2, 3]
b.extend([4, 5])
print(b) # [1, 2, 3, 4, 5](要素が展開されて追加される)
+ 演算子と append の違い:
ソート¶
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# sort: リストを直接並び替える(元のリストが変更される)
numbers.sort()
print(numbers) # [1, 1, 2, 3, 4, 5, 6, 9]
# 降順にソート
numbers.sort(reverse=True)
print(numbers) # [9, 6, 5, 4, 3, 2, 1, 1]
# sorted: 新しいリストを返す(元のリストは変更されない)
original = [3, 1, 4, 1, 5]
sorted_list = sorted(original)
print(original) # [3, 1, 4, 1, 5](変更されない)
print(sorted_list) # [1, 1, 3, 4, 5]
sort() と sorted() の使い分け
| メソッド/関数 | 元のリスト | 戻り値 | 使い場所 |
|---|---|---|---|
list.sort() |
変更される | None |
元のリストを直接並べ替えたいとき |
sorted(list) |
変更されない | 新しいリスト | 元のリストを残したいとき |
リストの結合とコピー¶
# リストの結合
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print(c) # [1, 2, 3, 4, 5, 6]
# extend: 別のリストの要素をすべて追加
a.extend(b)
print(a) # [1, 2, 3, 4, 5, 6]
# リストのコピー
original = [1, 2, 3]
copy = original.copy() # または list(original) や original[:]
copy.append(4)
print(original) # [1, 2, 3](元のリストは変わらない)
print(copy) # [1, 2, 3, 4]
よくある間違い
リストの代入はコピーではない(エイリアスの罠):
これは初心者が最も陥りやすい間違いの 1 つです。
# 間違い: = で代入すると、同じリストを共有する
a = [1, 2, 3]
b = a # b は a と同じリストを参照している(コピーではない)
b.append(4)
print(a) # [1, 2, 3, 4](a も変わってしまう!)
print(b) # [1, 2, 3, 4]
print(a is b) # True(同じオブジェクト)
# 正しい: コピーを作る
a = [1, 2, 3]
b = a.copy() # 独立したコピーを作成
b.append(4)
print(a) # [1, 2, 3](a は変わらない)
print(b) # [1, 2, 3, 4]
print(a is b) # False(別のオブジェクト)
コピーの方法は 3 通りあります:
original = [1, 2, 3]
copy1 = original.copy() # 方法 1: copy メソッド
copy2 = list(original) # 方法 2: list() コンストラクタ
copy3 = original[:] # 方法 3: スライス
ただし、リストの中にリストがある場合(ネストされたリスト)は、copy モジュールの deepcopy が必要です:
実行例¶
>>> fruits = ["りんご", "みかん", "ぶどう"]
>>> len(fruits)
3
>>> fruits.append("もも")
>>> fruits
['りんご', 'みかん', 'ぶどう', 'もも']
>>> fruits[1]
'みかん'
>>> fruits[-1]
'もも'
>>> "ぶどう" in fruits
True
>>> fruits.pop()
'もも'
>>> fruits
['りんご', 'みかん', 'ぶどう']
タプル(tuple)¶
タプルはリストに似ていますが、イミュータブル(変更不可)です。一度作成すると、要素の追加・変更・削除ができません。
タプルの作成¶
# 丸括弧で作成
point = (3, 5)
rgb = (255, 128, 0)
# 括弧を省略することもできる
coordinates = 10, 20
# 要素が 1 つのタプル(末尾にカンマが必要)
single = (42,)
よくある間違い
要素が 1 つのタプルでカンマを忘れる:
# 間違い: カンマがないとただの整数になる
not_a_tuple = (42)
print(type(not_a_tuple)) # <class 'int'>(タプルではない!)
# 正しい: カンマを忘れずにつける
single_tuple = (42,)
print(type(single_tuple)) # <class 'tuple'>
# 文字列でも同様
not_a_tuple = ("hello")
print(type(not_a_tuple)) # <class 'str'>
single_tuple = ("hello",)
print(type(single_tuple)) # <class 'tuple'>
タプルの操作¶
point = (3, 5, 7)
# インデックスでアクセス
print(point[0]) # 3
print(point[1]) # 5
# スライスも使える
print(point[1:]) # (5, 7)
# 長さ
print(len(point)) # 3
# アンパック(複数の変数に展開)
x, y, z = point
print(f"x={x}, y={y}, z={z}") # x=3, y=5, z=7
リストとタプルの比較¶
| 特徴 | リスト list |
タプル tuple |
|---|---|---|
| 記法 | [1, 2, 3] |
(1, 2, 3) |
| 変更可能か | はい(ミュータブル) | いいえ(イミュータブル) |
| 要素の追加・削除 | 可能 | 不可能 |
| 辞書のキーに使えるか | いいえ | はい |
| 用途 | 要素が変わる可能性があるデータ | 変わらないデータ(座標、RGB 等) |
タプルの使いどころ
- 変更されたくないデータ(座標、RGB 値など)を格納する場合
- 関数から複数の値を返す場合(
return a, bは実質タプルを返している) - 辞書のキーとして使う場合(リストは辞書のキーにできないが、タプルはできる)
辞書(dict)¶
辞書(ディクショナリ)は、キーと値のペアでデータを管理するデータ構造です。
辞書の作成¶
値へのアクセスと変更¶
student = {"name": "太郎", "age": 20, "grade": "B"}
# キーを指定して値を取得
print(student["name"]) # 太郎
print(student["age"]) # 20
# 値の変更
student["grade"] = "A"
print(student["grade"]) # A
# 新しいキーと値の追加
student["city"] = "横浜"
print(student)
# {'name': '太郎', 'age': 20, 'grade': 'A', 'city': '横浜'}
よくある間違い
存在しないキーにアクセスして KeyError:
student = {"name": "太郎", "age": 20}
# 間違い: 存在しないキーにアクセスするとエラー
# print(student["email"]) # KeyError: 'email'
# 正しい方法 1: get() メソッドを使う(キーがなければデフォルト値を返す)
print(student.get("email", "未登録")) # 未登録
# 正しい方法 2: in でキーの存在を確認してからアクセス
if "email" in student:
print(student["email"])
else:
print("email は登録されていません")
get メソッドで安全にアクセス
get() メソッドは、キーが存在しない場合にデフォルト値を返すため、KeyError を避けられます。第 2 引数を省略すると None が返ります。
辞書の主要メソッド¶
student = {"name": "太郎", "age": 20, "grade": "B"}
# keys: すべてのキーを取得
print(list(student.keys())) # ['name', 'age', 'grade']
# values: すべての値を取得
print(list(student.values())) # ['太郎', 20, 'B']
# items: キーと値のペアを取得
print(list(student.items())) # [('name', '太郎'), ('age', 20), ('grade', 'B')]
# in: キーの存在確認
print("name" in student) # True
print("email" in student) # False
# del: キーと値のペアを削除
del student["grade"]
print(student) # {'name': '太郎', 'age': 20}
# pop: キーを指定して値を取り出し、削除する
age = student.pop("age")
print(age) # 20
print(student) # {'name': '太郎'}
よくある間違い
辞書の in はキーを検索する(値ではない):
辞書のループ¶
scores = {"数学": 85, "英語": 72, "物理": 90}
# キーをループ
for subject in scores:
print(subject)
# キーと値を同時にループ
for subject, score in scores.items():
print(f"{subject}: {score}点")
# 出力:
# 数学: 85点
# 英語: 72点
# 物理: 90点
実行例¶
>>> d = {"a": 1, "b": 2, "c": 3}
>>> d["a"]
1
>>> d["d"] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> "b" in d
True
>>> d.get("x", 0)
0
>>> list(d.keys())
['a', 'b', 'c', 'd']
>>> for k, v in d.items():
... print(f"{k} -> {v}")
...
a -> 1
b -> 2
c -> 3
d -> 4
集合(set)¶
集合は、重複のない要素の集まりです。要素の順序は保証されません。
# 集合の作成
colors = {"赤", "青", "緑", "赤"} # 重複は自動的に除去される
print(colors) # {'赤', '青', '緑'}
# リストから重複を除去する
numbers = [1, 2, 2, 3, 3, 3, 4]
unique = set(numbers)
print(unique) # {1, 2, 3, 4}
print(list(unique)) # [1, 2, 3, 4](リストに戻す)
# 集合の演算
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # {1, 2, 3, 4, 5, 6}(和集合)
print(a & b) # {3, 4}(積集合)
print(a - b) # {1, 2}(差集合)
よくある間違い
空の集合を {} で作ろうとする:
リスト内包表記¶
リスト内包表記(list comprehension)は、リストを簡潔に生成するための構文です。
基本構文¶
# 通常の for ループで書く場合
squares = []
for x in range(5):
squares.append(x ** 2)
print(squares) # [0, 1, 4, 9, 16]
# リスト内包表記で書く場合(同じ結果を 1 行で)
squares = [x ** 2 for x in range(5)]
print(squares) # [0, 1, 4, 9, 16]
条件付きリスト内包表記¶
# 偶数だけを取り出す
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = [x for x in numbers if x % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
# 条件に応じて変換する
labels = ["偶数" if x % 2 == 0 else "奇数" for x in range(5)]
print(labels) # ['偶数', '奇数', '偶数', '奇数', '偶数']
実践例¶
# 文字列のリストを大文字に変換
words = ["hello", "world", "python"]
upper_words = [w.upper() for w in words]
print(upper_words) # ['HELLO', 'WORLD', 'PYTHON']
# 二次元リスト(3x3 の行列)を生成
matrix = [[0 for _ in range(3)] for _ in range(3)]
print(matrix) # [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
# 辞書内包表記
names = ["太郎", "花子", "次郎"]
scores = [85, 92, 78]
score_dict = {name: score for name, score in zip(names, scores)}
print(score_dict) # {'太郎': 85, '花子': 92, '次郎': 78}
リスト内包表記の使いどころ
リスト内包表記は、シンプルな変換やフィルタリングに適しています。処理が複雑になる場合は、可読性を重視して通常の for ループを使いましょう。
よくある間違いのまとめ¶
よくある間違い
1. リストの代入がコピーにならない:
a = [1, 2, 3]
b = a # コピーではなく同じオブジェクトへの参照
b.append(4)
print(a) # [1, 2, 3, 4](a も変わってしまう)
# 正しい:
b = a.copy() # 独立したコピーを作る
2. ループ中にリストを変更する:
# 間違い
nums = [1, 2, 3, 4, 5]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums) # 期待: [1, 3, 5] だが結果は予測不能
# 正しい: リスト内包表記で新しいリストを作る
nums = [1, 2, 3, 4, 5]
nums = [n for n in nums if n % 2 != 0]
print(nums) # [1, 3, 5]
3. 辞書の KeyError:
4. タプルの要素 1 つでカンマ忘れ:
5. append の戻り値を使おうとする:
# 間違い: append は None を返す
lst = [1, 2, 3]
new_lst = lst.append(4) # new_lst は None!
print(new_lst) # None
# 正しい: append は元のリストを変更する
lst = [1, 2, 3]
lst.append(4)
print(lst) # [1, 2, 3, 4]
6. 空の集合を {} で作ろうとする:
まとめ¶
- リストはミュータブルな順序付きコレクションで、
append,pop,sortなどのメソッドを持つ - インデックスは 0 から始まり、負のインデックスで末尾からアクセスできる
- スライス
[開始:終了:ステップ]でリストの一部を切り出せる - リストの代入(
b = a)はコピーではなくエイリアス。独立したコピーには.copy()を使う - タプルはイミュータブルなリストであり、変更されないデータに適している。要素 1 つのタプルにはカンマが必要
- 辞書はキーと値のペアでデータを管理し、キーで高速にアクセスできる。存在しないキーには
get()を使う - 集合は重複のない要素の集まりで、和集合・積集合などの集合演算ができる。空の集合は
set()で作る - リスト内包表記を使うと、リストの生成を簡潔に記述できる